Consider this piece of C++11 code:
#include <iostream>
struct X
{
X(bool arg) { std::cout << arg << '\n'; }
};
int main()
{
double d = 7.0;
X x{d};
}
There's a narrowing conversion from a double to a bool in the initialization of x
. According to my understanding of the standard, this is ill-formed code and we should see some diagnostic.
Visual C++ 2013 issues an error:
error C2398: Element '1': conversion from 'double' to 'bool' requires a narrowing conversion
However, both Clang 3.5.0 and GCC 4.9.1, using the following options
-Wall -Wextra -std=c++11 -pedantic
compile this code with no errors and no warnings. Running the program outputs a 1
(no surprise there).
Now, let's go deeper into strange territory.
Change X(bool arg)
to X(int arg)
and, suddenly, we've got an error from Clang
error: type 'double' cannot be narrowed to 'int' in initializer list [-Wc++11-narrowing]
and a warning from GCC
warning: narrowing conversion of 'd' from 'double' to 'int' inside { } [-Wnarrowing]
This looks more like what I was expecting.
Now, keep the bool
constructor argument (that is, revert to X(bool arg)
), and change double d = 7.0;
to int d = 7;
. Again, a narrowing error from Clang, but GCC doesn't issue any diagnostic at all and compiles the code.
There are a few more behaviour variants that we can get if we pass the constant directly to the constructor, some strange, some expected, but I won't list them here - this question is getting too long as it is.
I'd say this is one of the rare cases when VC++ is right and Clang and GCC are wrong when it comes to standard-conformance, but, given the respective track records of these compilers, I'm still very hesitant about this.
What do the experts think?
Standard references (quotes from the final standard document for C++11, ISO/IEC 14882-2011):
In 8.5.4 [dcl.init.list] paragraph 3, we have:
— Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution (13.3, 13.3.1.7). If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed.
In the same section, in paragraph 7, we have:
A narrowing conversion is an implicit conversion
— from a floating-point type to an integer type, or
— from long double to double or float, or from double to float, except where the source is a constant expression and the actual value after conversion is within the range of values that can be represented (even if it cannot be represented exactly), or
— from an integer type or unscoped enumeration type to a floating-point type, except where the source is a constant expression and the actual value after conversion will fit into the target type and will produce the original value when converted back to the original type, or
— from an integer type or unscoped enumeration type to an integer type that cannot represent all the values of the original type, except where the source is a constant expression and the actual value after conversion will fit into the target type and will produce the original value when converted back to the original type.
[ Note: As indicated above, such conversions are not allowed at the top level in list-initializations.—end note ]
In 3.9.1 [basic.fundamental] paragraph 7, we have:
Types bool, char, char16_t, char32_t, wchar_t, and the signed and unsigned integer types are collectively called integral types.48 A synonym for integral type is integer type.
(I was starting to question everything at this stage...)
This simply looks like a bug, if we try the following:
both
gcc
andclang
issue a diagnostic, for examplegcc
says:This is covered in the draft C++11 standard by section
8.5.4
List-initialization paragraph 7 which says:This is same paragraph that covers your example and the following simpler example:
which would be covered by this bullet from paragraph
7
quoted above:From paragraph
3
, this is ill-formed an requires a diagnostic:which
gcc
produces no diagnostic butclang
does provide the following warning, although not the narrowing conversion warning we should see:Note, section
3.9.1
[basic.fundamental] says:You should file a bug report with both clang and gcc.
Jonathan Wakely notes that the EDG compiler gives a narrowing error for the OPs code, which is a strong indication that this indeed should produce a diagnostic.
Update
I submitted a gcc and clang bug report.
The clang bug report has been updated as fixed:
The gcc bug report has been updated as fixed:
and a live example seems to confirm this.