Given the following program:
#include <cmath>
int main()
{
std::abs(0u) ;
}
gcc and clang disagree on whether this is ill-formed. Using gcc with libstdc++ the code builds without error or warning (see it live), while using clang with libc++ it generates the following error (see it live):
error: call to 'abs' is ambiguous
std::abs(0u) ;
^~~~~~~~
Which result is correct? Should abs(0u) be ambiguous or not?
MSalters points out an interesting related question: Template version of std::abs.
Looks like libstdc++ is correct, this is not ill-formed, although we will see there are some doubts expressed over whether this is a defect in LWG active issue 2192.
The draft C++11 standard section 26.8 [c.math] paragraph 11 says:
Moreover, there shall be additional overloads sufficient to ensure:
and includes the following item:
Otherwise, if any argument corresponding to a double parameter has type double or an integer type, then all arguments corresponding to
double parameters are effectively cast to double.
and we can see this libstdc++ does indeed provide for this case:
template<typename _Tp>
inline typename __gnu_cxx::__enable_if<__is_integer<_Tp>::__value,
double>::__type
abs(_Tp __x)
{ return __builtin_fabs(__x); }
There is a also a gcc bug report std::abs (long long) resorts to std::abs (double) if llabs is absent, which questions if this implementation is correct and one response says:
[...]is fine per the Standard, any integer is supposed to
unconditionally become double. [...]
The bug report eventually lead to LWG active issue 2192: Validity and return type of std::abs(0u) is unclear being filed which says amongst other things:
In C++11 the additional "sufficient overload" rule from 26.8 [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 26.8 [c.math] p11 (Note that the current
resolution of LWG 2086 doesn't fix this problem).
Any translation unit including both and 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 [...] 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 and , so cannot be applied to the abs
functions (but to the fabs functions!).
The question is whether this was intended to apply to abs as well. This could be a defect since there does not seem to a way to interpret the current wording to exclude abs.
So current wording indicates libstdc++ is conformant, it is not clear why libc++ has chosen their current implementation as it is. I can find no bug reports nor discussions involving this topic and the LWG issue does not mention diverging implementations.
The proposed solution would make std::abs(0u) ill-formed:
If abs() is called with an argument of unsigned integral type that
cannot be converted to int by integral promotion ([conv.prom]), the
program is ill-formed. [Note: arguments that can be promoted to int
are permitted for compatibility with C. — end note]
While some may question the notion of using abs with an unsigned type Howard Hinnant points out in the report that when using templates such consequences may not be apparent and provides an example:
[...]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)
{
// ...
}
// ...
}
Related
Recent versions of clang (since clang-11) issue a warning when compiled with -pedantic for the following segment of code:
namespace foo {
template <int A>
struct Bar {
~Bar();
};
} // namespace foo
template <int A>
foo::Bar<A>::~Bar(){}
With the generated warning (and error with -Werror) being:
<source>:10:12: error: ISO C++ requires the name after '::~' to be found in the same scope as the name before '::~' [-Werror,-Wdtor-name]
foo::Bar<A>::~Bar(){}
~~~~~~~~~~~^~
::Bar
Live Example
clang is the first compiler I've seen to issue this diagnostic, and as far as I'm aware, what was written above is completely valid C++. Two different ways that seem to suppress this is to either define within the same namespace or explicitly qualify the destructor name -- such as:
...
template <int A>
foo::Bar<A>::~Bar<A>(){}
Is Clang's diagnostic here correct? It's my understanding that the type's name would absolutely be in the correct name-scope as foo::Bar<A>::~ -- and that the qualification of ~Bar<A>() should be unnecessary.
From what I have learned since posting the question, this warning is, strictly-speaking, correct -- though it most likely a defect in the wording of the standard.
According to Richard Smith in LLVM Bug 46979:
The diagnostic is correct, per the standard rules as written; strictly-speaking, the C++ rules require this destructor to be written as
template<typename T>
A::B<T>::~B<T>()
Per C++ [basic.lookup.qual]p6:
In a qualified-id of the form:
nested-name-specifier[opt] type-name :: ~ type-name
the second type-name is looked up in the same scope as the first.
This means that the second B is looked up in class A, which finds only the class template B and not the injected-class-name.
This is not an especially useful rule, and likely isn't the intended rule, which is why this diagnostic (along with a bunch of similar diagnostics for plausible but formally incorrect destructor names) is disabled by default but included in -pedantic.
Looking further into this, I can find the mentioned passage for [basic.lookup.qual]/6 in the C++20 standard, but it appears drafts for C++23 have changed this -- which points towards this most likely being a defect.
In drafts for [basic.lookup.qual] for C++23, this whole section has been overhauled and is currently, at the time of writing, replaced with [basic.lookup.qual]/4 which states:
If a qualified name Q follows a ~:
(4.1) If Q is a member-qualified name, it undergoes unqualified lookup as well as qualified lookup.
(4.2) Otherwise, its nested-name-specifier N shall nominate a type.
If N has another nested-name-specifier S, Q is looked up as if its lookup context were that nominated by S.
(4.3) Otherwise, if the terminal name of N is a member-qualified name M, Q is looked up as if ~Q appeared in place of M (as above).
(4.4) Otherwise, Q undergoes unqualified lookup.
(4.5) Each lookup for Q considers only types (if Q is not followed by a <) and templates whose specializations are types.
If it finds nothing or is ambiguous, it is discarded.
(4.6) The type-name that is or contains Q shall refer to its (original) lookup context (ignoring cv-qualification) under the interpretation established by at least one (successful) lookup performed.
[Example 4:
struct C {
typedef int I;
};
typedef int I1, I2;
extern int* p;
extern int* q;
void f() {
p->C::I::~I(); // I is looked up in the scope of C
q->I1::~I2(); // I2 is found by unqualified lookup
}
struct A {
~A();
};
typedef A AB;
int main() {
AB* p;
p->AB::~AB(); // explicitly calls the destructor for A
}
— end example]
(The full quote has been posted here since this is from a draft, and the wording may be subject to change in the future)
This appears to explicitly correct this issue by ensuring that lookup is performed as one would expect.
So basically the diagnostic is correct under older versions of C++ due to a defect in the wording -- but C++23 appears to change that. I am not sure whether this will be retroactively fixed in older versions as a defect given that it appears no compiler actually follows this pedantic requirement.
Recent versions of clang (since clang-11) issue a warning when compiled with -pedantic for the following segment of code:
namespace foo {
template <int A>
struct Bar {
~Bar();
};
} // namespace foo
template <int A>
foo::Bar<A>::~Bar(){}
With the generated warning (and error with -Werror) being:
<source>:10:12: error: ISO C++ requires the name after '::~' to be found in the same scope as the name before '::~' [-Werror,-Wdtor-name]
foo::Bar<A>::~Bar(){}
~~~~~~~~~~~^~
::Bar
Live Example
clang is the first compiler I've seen to issue this diagnostic, and as far as I'm aware, what was written above is completely valid C++. Two different ways that seem to suppress this is to either define within the same namespace or explicitly qualify the destructor name -- such as:
...
template <int A>
foo::Bar<A>::~Bar<A>(){}
Is Clang's diagnostic here correct? It's my understanding that the type's name would absolutely be in the correct name-scope as foo::Bar<A>::~ -- and that the qualification of ~Bar<A>() should be unnecessary.
From what I have learned since posting the question, this warning is, strictly-speaking, correct -- though it most likely a defect in the wording of the standard.
According to Richard Smith in LLVM Bug 46979:
The diagnostic is correct, per the standard rules as written; strictly-speaking, the C++ rules require this destructor to be written as
template<typename T>
A::B<T>::~B<T>()
Per C++ [basic.lookup.qual]p6:
In a qualified-id of the form:
nested-name-specifier[opt] type-name :: ~ type-name
the second type-name is looked up in the same scope as the first.
This means that the second B is looked up in class A, which finds only the class template B and not the injected-class-name.
This is not an especially useful rule, and likely isn't the intended rule, which is why this diagnostic (along with a bunch of similar diagnostics for plausible but formally incorrect destructor names) is disabled by default but included in -pedantic.
Looking further into this, I can find the mentioned passage for [basic.lookup.qual]/6 in the C++20 standard, but it appears drafts for C++23 have changed this -- which points towards this most likely being a defect.
In drafts for [basic.lookup.qual] for C++23, this whole section has been overhauled and is currently, at the time of writing, replaced with [basic.lookup.qual]/4 which states:
If a qualified name Q follows a ~:
(4.1) If Q is a member-qualified name, it undergoes unqualified lookup as well as qualified lookup.
(4.2) Otherwise, its nested-name-specifier N shall nominate a type.
If N has another nested-name-specifier S, Q is looked up as if its lookup context were that nominated by S.
(4.3) Otherwise, if the terminal name of N is a member-qualified name M, Q is looked up as if ~Q appeared in place of M (as above).
(4.4) Otherwise, Q undergoes unqualified lookup.
(4.5) Each lookup for Q considers only types (if Q is not followed by a <) and templates whose specializations are types.
If it finds nothing or is ambiguous, it is discarded.
(4.6) The type-name that is or contains Q shall refer to its (original) lookup context (ignoring cv-qualification) under the interpretation established by at least one (successful) lookup performed.
[Example 4:
struct C {
typedef int I;
};
typedef int I1, I2;
extern int* p;
extern int* q;
void f() {
p->C::I::~I(); // I is looked up in the scope of C
q->I1::~I2(); // I2 is found by unqualified lookup
}
struct A {
~A();
};
typedef A AB;
int main() {
AB* p;
p->AB::~AB(); // explicitly calls the destructor for A
}
— end example]
(The full quote has been posted here since this is from a draft, and the wording may be subject to change in the future)
This appears to explicitly correct this issue by ensuring that lookup is performed as one would expect.
So basically the diagnostic is correct under older versions of C++ due to a defect in the wording -- but C++23 appears to change that. I am not sure whether this will be retroactively fixed in older versions as a defect given that it appears no compiler actually follows this pedantic requirement.
The following code works without warning:
std::chrono::duration<unsigned int> d{-17};
I would expect the same warning I get from:
unsigned int x = -17;
Here's the relevant code:
template<typename _Rep2, typename = typename
enable_if<is_convertible<_Rep2, rep>::value
&& (treat_as_floating_point<rep>::value
|| !treat_as_floating_point<_Rep2>::value)>::type>
explicit duration(const _Rep2& __rep)
: __r(static_cast<rep>(__rep)) { }
The static_cast is hiding warnings, and it seems to me that it isn't required for any functionality the standard mandates. Is this just a libc++ problem, or is it required to work this way by the standard?
This is behavior as expected by the standard. The remarks on that constructor are:
This constructor shall not participate in overload resolution unless Rep2 is implicitly
convertible to rep and
(1.1) — treat_as_floating_point_v<rep> is true or
(1.2) — treat_as_floating_point_v<Rep2> is false.
int is implicitly convertible to unsigned int and treat_as_floating_point<int> is false, so we're fine.
The effects are:
Postcondition: count() == static_cast<rep>(r).
libc++ and libstdc++ are both conforming by allowing the code you wrote. It is well-formed. If you think it should be ill-formed, you should submit an issue about it. This isn't a compiler bug. It may be a standard bug.
I found an interesting gotcha with std::numeric_limits<seconds>::max() returning 0. The answer is to use seconds::max() or std::numeric_limits<seconds::rep>::max() instead, but I am interested to know why this happens. I would expect it to either fail at compile time or just work. The following code demonstrates the issue with gcc 4.9.3.
#include <iostream>
#include <limits>
#include <chrono>
using namespace std;
using namespace std::chrono;
int main(int /*argc*/, const char* /*argv*/[])
{
const auto maxSeconds = std::numeric_limits<seconds>::max();
std::cerr << maxSeconds.count() << "\n";
const auto maxSeconds2 = seconds::max();
std::cerr << maxSeconds2.count() << "\n";
return 0;
}
I can't see any implicit conversions in the chrono header file. If a duration had implicitly cast to a numeric type and the sign was lost or a bool you could end up with a minimum of zero - but a maximum of zero doesn't make sense.
As TartanLlama points out the default specialization uses the default constructor and therefore returns 0.
Delving into an old copy of the standard I see the following dictats:
18.3.2.3 Class template numeric_limits [numeric.limits]
Non-arithmetic standard types, such as complex<T> (26.4.2), shall not have specializations.
and a little later:
The default numeric_limits<T> template shall have all members, but with 0 or false values.
The value of each member of a specialization of numeric_limits on a cv-qualified type cv T shall be equal to the value of the
corresponding member of the specialization on the unqualified type
T.
What is missing is an explanation of why this was considered a better idea by the committee than a compilation failure. Is a library defect report warranted?
Update: I have raised this as an issue with the ISO committee
https://issues.isocpp.org/show_bug.cgi?id=186
std::numeric_limits is not specialized for std::chrono::seconds. Default definitions are given for all data members and functions in std::numeric_limits to avoid compiler errors for unspecialized types. The default version of numeric_limits<T>::max() simply returns T(), which is 0 in this case.
You can check if std::numeric_limits is specialized for a given T at compile time by checking std::numeric_limits<T>::is_specialized, which defaults to false.
std::chrono::seconds itself is not a standard arithmetic type, thus std::numeric_limits is not specialized for it. So you just see some rather useless defaults.
To query the range of the underlying type used to count the ticks (which, under gcc, is the 64 bit long int), use
std::numeric_limits<seconds::rep>::max();
instead.
Consider the following program:
extern int x;
auto x = 42;
int main() { }
Clang 3.5 accepts it (live demo), GCC 4.9 and VS2013 do not (live demo for the former). Who is right, and where is the correct behavior specified in the C++ Standard?
There's surprisingly little in the standard about this. About all we hear about redeclaration is:
[C++11: 3.1/1]: A declaration (Clause 7) may introduce one or more names into a translation unit or redeclare names introduced by previous declarations. [..]
and the only relevant part of auto's semantics:
[C++11: 7.1.6.4/3]: Otherwise, the type of the variable is deduced from its initializer. [..]
(reminding us that the type of x is int).
We know that a variable must be given the same type by all declarations:
[C++11: 3.5/10]: After all adjustments of types (during which typedefs (7.1.3) are replaced by their definitions), the types specified by all declarations referring to a given variable or function shall be identical, except that declarations for an array object can specify array types that differ by the presence or absence of a major array bound (8.3.4). A violation of this rule on type identity does not require a diagnostic.
and the "after all adjustments of types" ought to take care of any questions regarding auto's participation in all of this; my interpretation, then, is that this is inherently a valid redeclaration (and definition) of the x at global scope with type int, and that Clang is correct. Even if we propose that auto does not count as "adjustment of type", since no diagnostic is required, at worst all listed implementations are compliant in their own way.
I believe GCC and Visual Studio are taking the following as inspiration:
[C++11: 7.1.6.4/5]: A program that uses auto in a context not explicitly allowed in this section is ill-formed.
…but I think that this is short-sighted. It seems unlikely that the standard language is intended to prohibit the usual redeclaration rules, just because they are not repeated or explicitly referenced from within 7.1.6.4.
C++14 adds wording that relates to declarations of functions with deduced types:
[C++14: 7.1.6.4/13]: Redeclarations or specializations of a function or function template with a declared return type that uses a placeholder type shall also use that placeholder, not a deduced type. [..]
By symmetry one might suggest that, in your int case, it is intended that GCC and VS be correct in rejecting the program. However, this is a different feature (since deduction cannot be applied to mere declarations) and thus a different scenario.
Either way, improved standard wording would help here. I consider it a [reasonably minor] editorial defect.
Note
I answered a question that was closed a duplicate of this one. I asked for merge and was told instead to provide an answer here. See below for my original answer.
Update clang is correct
I asked this question on twitter and the response I received from Richard Smith was as follows:
Not a defect, it's intentional that this restriction applies only to deduced return types and not to variable types. For variables, it's just a convenience shorthand, but return type deduction affects something more fundamental about functions (and especially function templates).
So the logic is that this is allowed by [dcl.spec.auto] and to restrict this for deduced return types paragraph [dcl.spec.auto]p11 was added to the section. Otherwise there is no restriction and therefore this is not restricted for the variables case.
Original
Currently [dcl.spec.auto] does not seem to cover this case explictly but it does say in [dcl.spec.auto]p5:
A program that uses auto or decltype(auto) in a context not explicitly allowed in this subclause is ill-formed.
and we can see it makes a similar case for functions ill-formed in [dcl.spec.auto]p11:
Redeclarations or specializations of a function or function template
with a declared return type that uses a placeholder type shall also
use that placeholder, not a deduced type. Similarly, redeclarations or
specializations of a function or function template with a declared
return type that does not use a placeholder type shall not use a
placeholder. [ Example:
auto f();
auto f() { return 42; } // return type is int
auto f(); // OK
int f(); // error, cannot be overloaded with auto f()
....
So although this could use clarification as currently worded it feels like gcc is correct and this is ill-formed.
I'd imagine the restriction in [dcl.spec.auto]p11 exists because otherwise, that would allow:
int f();
auto f(); // What's the return type here?
The thing is, you can have an undeduced type type has the return type of a function. There are no deduction rules based on previous declarations, which is why such mixing is disallowed for functions, even though the following would be perfectly fine:
int f();
auto f() { return 1; }
This problem does not exist for variables:
extern int v;
extern auto v; // ill-formed
Any declaration-only variables has to use a non-placeholder type. What this means is that if you use a placeholder type for the definition of v, it can get deduced without any problems and then of course has to match the non-placeholder type used in the first declaration.
extern int v;
auto v = 1; // ok, type deduced as 'int', matches first declaration.