std::abs(0u)
is unclearSection: 29.7 [c.math] Status: C++17 Submitter: Daniel Krügler Opened: 2012-10-02 Last modified: 2017-07-30
Priority: 2
View all other issues in [c.math].
View all issues with C++17 status.
Discussion:
In C++03 the following two programs are invalid:
#include <cmath> int main() { std::abs(0u); }
#include <cstdlib> int main() { std::abs(0u); }
because none of the std::abs()
overloads is a best match.
In C++11 the additional "sufficient overload" rule from 29.7 [c.math] p11 (see also LWG
2086) can be read to be applicable to the std::abs()
overloads as well, which
can lead to the following possible conclusions:
The program
#include <type_traits> #include <cmath> static_assert(std::is_same<decltype(std::abs(0u)), double>(), "Oops"); int main() { std::abs(0u); // Calls std::abs(double) }
is required to be well-formed, because of sub-bullet 2 ("[..] or an integer type [..]") of 29.7 [c.math] p11 (Note that the current resolution of LWG 2086 doesn't fix this problem).
Any translation unit including both <cmath>
and <cstdlib>
might be ill-formed because of two conflicting requirements for the return type of the overload
std::abs(int)
.
It seems to me that at least the second outcome is not intended, personally I think that both
are unfortunate: In contrast to all other floating-point functions explicitly listed in sub-clause
29.7 [c.math], the abs
overloads have a special and well-defined meaning for
signed integers and thus have explicit overloads returning a signed integral type. I also believe that
there is no problem accepting that std::fabs(0u)
is well-defined with return type double
,
because the leading 'f' clearly signals that we have a floating point function here. But the expected
return type of std::abs(0u)
seems less than clear to me. A very reasonable answer could be that
this has the same type as its argument type, alternatively it could be a reasonably chosen signed
integer type, or a floating point type. It should also be noted, that the corresponding
"generic type function" rule set from C99/C1x in 7.25 p2+3 is restricted to the floating-point functions
from <math.h>
and <complex.h>
, so cannot be applied to the abs
functions (but to the fabs
functions!).
unsigned int
, but there would be no clear answer for the input type std::uintmax_t
.
Based on this it seems to me that the C++03 state in regard to unsigned integer values was the better situation, alerting the user that this code is ambigious at the moment (This might be change with different core-language rules as described in N3387).
[2013-04-20, Bristol]
Resolution: leave as new and bring it back in Chicago.
[2013-09 Chicago]
This issue also relates to LWG 2294
STL: these two issues should be bundled Stefanus: do what Pete says, and add overloads for unsigned to return directly STL: agree Consensus that this is an issue Walter: motion to move to Open STL: no wording for 2294 Stefanus: move to open and note the 2 issues are related and should be moved together Stefanus: add and define unsigned versions ofabs()
[2014-02-03 Howard comments]
Defining abs()
for unsigned integers is a bad idea. Doing so would turn compile time errors into run time errors,
especially in C++ where we have templates, and the types involved are not always apparent to the programmer at design time.
For example, consider:
template <class Int>
Int
analyze(Int x, Int y)
{
// ...
if (std::abs(x - y) < threshold)
{
// ...
}
// ...
}
std::abs(expr)
is often used to ask: Are these two numbers sufficiently close? When the assumption is that
the two numbers are signed (either signed integral, or floating point), the logic is sound. But when the same logic is
accidentally used with an arithmetic type not capable of representing negative numbers, and especially if unsigned overflow
will silently happen, then the logic is no longer correct:
auto i = analyze(20u, 21u); // Today a compile time error // But withabs(unsigned)
becomes a run time error
This is not idle speculation. Search the net for "abs unsigned
"
here or
here.
chrono
duration
s and time_point
s are allowed to be based on unsigned integers. Taking the
absolute value of the difference of two such time_point
s would be easy to accidentally do (say in code templated on
time_point
s), and would certainly be a logic bug, caught at compile time unless we provide the error prone abs(unsigned)
.
[2015-02, Cologne]
GR: Do we want to make the changes to both <cmath>
and <cstdlib>
?
AM: I think so; we should provide consistent overloads.
GR: Then we're imposing restrictions on what users put in the global namespace.
AM: I'm not so worried about that. Users already know not to use C library names.
VV: So what are we going to do about unsigned integers? AM: We will say that they are ill-formed.
AM: Does anyone volunteer to send updated wording to Daniel? GR, can you do it? GR: Sure.
GR: To clarify: we want to make unsigned types ill-formed?
AM: With promotion rank at least unsigned int
.
GR: And NL suggests to just list those types.
This wording is relative to N3376.
Change 29.7 [c.math] p11 as indicated:
-11- Moreover, except for the
[…]abs
functions, there shall be additional overloads sufficient to ensure:
[2015-03-03, Geoffrey Romer provides improved wording]
In the following I've drafted combined wording to resolve LWG 2192 and 2294. Note that the first two paragraphs are taken verbatim from the P/R of LWG 2294, but the third is newly drafted:
[2015-05-05 Lenexa: Howard to draft updated wording]
[2015-09-11: Howard updated wording]
[2015-10, Kona Saturday afternoon]
HH: abs() for unsigned types is really dangerous. People often use abs(x - y), which would be a disaster.
TK: That's why you need a two-argument abs_diff(x, y), especially for unsigned types.
JW: Lawrence has a proposal for abs_diff in the mailing.
STL: As an alternative to considering promotions, I would just ban all unsigned types, even unsigned char.
STL: Does the PR change any implementation? Is the final paragraph just a consequence?
HH: It's a consequence. It could just be a note.
VV: Ship it as is.
STL: Editorial: capitalize the first letter in the Note.
Move to Tentatively ready.
Proposed resolution:
This wording is relative to N4527.
Insert the following new paragraphs after 29.7 [c.math] p7:
-6- In addition to the
-7- The added signatures are:int
versions of certain math functions in<cstdlib>
, C++ addslong
andlong long
overloaded versions of these functions, with the same semantics.long abs(long); // labs() long long abs(long long); // llabs() ldiv_t div(long, long); // ldiv() lldiv_t div(long long, long long); // lldiv()-?- To avoid ambiguities, C++ also adds the following overloads of
abs()
to<cstdlib>
, with the semantics defined in<cmath>
:float abs(float); double abs(double); long double abs(long double);-?- To avoid ambiguities, C++ also adds the following overloads of
abs()
to<cmath>
, with the semantics defined in<cstdlib>
:int abs(int); long abs(long); long long abs(long long);-?- If
abs()
is called with an argument of typeX
for whichis_unsigned<X>::value
istrue
and ifX
cannot be converted toint
by integral promotion (7.3.7 [conv.prom]), the program is ill-formed. [Note: arguments that can be promoted toint
are permitted for compatibility with C. — end note]