25

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...)

9
  • Hey, where did all the comments go? Some of them contained useful information for diagnosing the problem, for Clang in particular. Commented Dec 17, 2014 at 14:41
  • 1
    Filed clang and gcc bug report. Commented Feb 12, 2015 at 16:37
  • 1
    clang closed bug report as fixed. Commented Feb 19, 2015 at 15:02
  • 1
    @ShafikYaghmour Good stuff, thanks for taking care of this. Commented Feb 19, 2015 at 16:40
  • 1
    Looks like both gcc and clang has fixed this. Commented Aug 24, 2018 at 16:35

1 Answer 1

16

This simply looks like a bug, if we try the following:

bool b {3} ; 

both gcc and clang issue a diagnostic, for example gcc says:

warning: narrowing conversion of '3' from 'int' to 'bool' inside { } [-Wnarrowing]

This is covered in the draft C++11 standard by section 8.5.4 List-initialization paragraph 7 which says:

A narrowing conversion is an implicit conversion

[...]

  • 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.

This is same paragraph that covers your example and the following simpler example:

bool a {3.0} ; 

which would be covered by this bullet from paragraph 7 quoted above:

  • from a floating-point type to an integer type, or

From paragraph 3, this is ill-formed an requires a diagnostic:

List-initialization of an object or reference of type T is defined as follows:

[...]

  • Otherwise, if the initializer list has a single element, the object or reference is initialized from that element; if a narrowing conversion (see below) is required to convert the element to T, the program is ill-formed.

which gcc produces no diagnostic but clang does provide the following warning, although not the narrowing conversion warning we should see:

warning: implicit conversion from 'double' to 'bool' changes value from 3 to true [-Wliteral-conversion]

Note, section 3.9.1 [basic.fundamental] says:

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.

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:

Fixed in r229792.

The gcc bug report has been updated as fixed:

Fixed.

and a live example seems to confirm this.

Sign up to request clarification or add additional context in comments.

1 Comment

Yeah, the warning for initializing a bool with a constant double falls under the 'few more behaviour variants' I mentioned in the question. Basically, in this case, Clang still doesn't detect that this is a narrowing conversion in list-initialization, but falls back to the same warning it issues for any such initialization with a constant value (it will issue the same warning if you replace the braces with parentheses, making this an allowed implicit conversion).

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.