Given the following user-defined type S
with a conversion function to double:
struct S
{
operator double() { return 1.0;}
};
and the following calls to cmath functions using the type S
:
#include <cmath>
void test(S s) {
std::sqrt(s);
std::log(s);
std::isgreater(s,1.0);
std::isless(s,1.0);
std::isfinite(s) ;
}
This code compiles with gcc
using libstdc++
(see it live) but with clang
using libc++
it generates errors for several of the calls (see it live) with the following error for isgreater:
error: no matching function for call to 'isgreater'
std::isgreater(s,1.0);
^~~~~~~~~~~~~~
note: candidate template ignored: disabled by 'enable_if' [with _A1 = S, _A2 = double]
std::is_arithmetic<_A1>::value &&
^
and similar errors for isless and isfinite, so libc++
expects the arguments for those calls to be arithmetic types which S
is not, we can confirm this by going to the source for libc++ cmath header. Although, the requirement for arithmetic types is not consistent across all the cmath
functions in libc++
.
So the question is, is it valid to pass non-arithmetic types as arguments to cmath
functions?
TL;DR
According to the standard it is valid to pass non-arithmetic types as arguments to
cmath
functions but defect report2068
argues the original intent was thatcmath
functions should be restricted to arithmetic types and it appears possible using non-arithmetic arguments will eventually be made ill-formed. So although technically valid using non-arithmetic types as arguments seems questionable in light of defect report2068
.Details
The cmath header is covered in the draft standard section
26.8
[c.math] provides an additional float and long double overload for the each function defined in math.h that takes a double argument and further, paragraph11
provides for sufficient overloads and says:This seems valid in C++11
In C++11 section
26.8
[c.math] does not include any restrictions disallowing non-arithmetic arguments tocmath
functions. In each case from the question we have an overload available which takes double argument(s) and these should be selected via overload resolution.Defect report 2086
But for C++14 we have defect report 2086: Overly generic type support for math functions, which argues that the original intent of section
26.8
[c.math] was to limitcmath
functions to be valid only for arithmetic types, which would mimic how they worked in C:and says:
and reworded section
26.8
paragraph11
to say (emphasis mine):So this is invalid in C++14?
Well, despite the intent it looks technically to still be valid as argued in this comment from the discussion in libc++ bug report: incorrect implementation of isnan and similar functions:
So, the rewording by
DR 2086
of paragraph 11 does not make it ill-formed to call the float, double and long double overloads available otherwise with non-arithmetic arguments.Technically valid but questionable to use
So although the C++11 and C++14 standard do not restrict
cmath
functions to arithmetic argumentsDR 2068
argues the intent of26.8
paragraph11
was to restrictcmath
functions to take only arithmetic arguments and apparently intended to close the loophole in C++14, but did not provide strong enough restrictions.It seems questionable to rely on a feature which could become ill-formed in a future version of the standard. Since we have implementation divergence any code that relies on passing non-arithmetic arguments to
cmath
functions for those cases is non-portable and so will be useful only in limited situations. We have an alternative solution, which is to explicitly cast non-arithmetic types to arithmetic types, which bypasses the whole issue, we no longer have to worry about the code becoming ill-formed and it is portable:As Potatoswatter points out using unary
+
is also an option:Update
As T.C. points out in C++11 it can be argued that
26.8
paragraph11
bullet3
applies since the argument is neither long double, double nor an integer and should therefore the arguments of typeS
should be cast to float first. Note, as indicated by the defect reportgcc
never implemented this and as far I know neither didclang
.