7

Using the {} initializer in C++11 to initialize bool b = {2} yields the following warning message:

warning: narrowing conversion of ‘2’ from ‘int’ to ‘bool’ inside { } [-Wnarrowing]

However, using the old style bool b = 2 has no such problem. What is the reason behind this?


Update: I compiled the code using g++ -std=c++11 and it gave me the warning. If I add the option -pedantic-errors, the warning becomes an error.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
tianz
  • 2,155
  • 2
  • 19
  • 28
  • 2
    When I really think about this, although we are used to thinking about `anything not 0` and `0` as `true` and `false` as far as I can tell the standard gives you no promise that `true` and `false` are `1` and `0` respectively, it just guarantees conversions. So if you are going to use a literal then use `true` and `false`, which will never be a narrowing conversion. – Shafik Yaghmour Nov 02 '13 at 17:21

2 Answers2

8

Narrowing a data type in an initialization-list makes your c++11 program ill formed, in this situation the compiler can either give a warning or keep going.

Interestingly enough you can actually change it to bool b = {1} and there is no warning, I'm assuming because the value of a bool is guaranteed to convert to 0 and 1 in integral types.

Here is a standard quote confirming the error.

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.
As indicated above, such conversions are not allowed at the top level in list-initializations

aaronman
  • 18,343
  • 7
  • 63
  • 78
  • The `bool b = {2}` was legal since C++98 and narrowing conversion were not errors in C++98. Making it an error in C++11 would constitute a breaking change. Did they really do it in C++11? I truly don't know, but I suspect that in case of `bool b = {2}` it is not an error. This is probably why a mere warning is issued in this case. (Correct me if I'm wrong.) – AnT stands with Russia Nov 02 '13 at 01:24
  • @AndreyT it is an error on clang at least, on gcc haven't checked – aaronman Nov 02 '13 at 01:25
  • OK, it looks like it should be an error indeed. They probably thought that the `{}` syntax was used very rarely to initialize scalars (if at all), so making it an error would not break much of legacy code. – AnT stands with Russia Nov 02 '13 at 01:26
  • I compiled the code using `g++ -std=c++11` and it gave me a warning instead of an error – tianz Nov 02 '13 at 01:28
  • @TianZhou should be an error, are you sure the compiler isn't ignoring the -std=c++11 option – aaronman Nov 02 '13 at 01:29
  • @TianZhou it would say it, what is your compiler version – aaronman Nov 02 '13 at 01:30
  • @AndreyT Oh I just added that option and the warning became an error! – tianz Nov 02 '13 at 01:31
  • @aaronman: The value of `bool` in C++ is not guaranteed to be `0` or `1`. It is guaranteed to convert to `0` or `1` when converted to integral types. Meanwhile, the internal representation of `bool` can be anything. – AnT stands with Russia Nov 02 '13 at 01:32
  • @AndreyT fair enough, fixed – aaronman Nov 02 '13 at 01:32
  • @TianZhou it's not too, old IDT (don't use gcc myself) but as the standard quote in the deleted answer said the error is indeed correct, I'll put a standard quote in my answer too – aaronman Nov 02 '13 at 01:34
  • @ShafikYaghmour clang produces an error my gcc isn't up to date enough to error – aaronman Nov 02 '13 at 02:00
  • @ShafikYaghmour I'll amend my answer – aaronman Nov 02 '13 at 02:02
  • @aaronman I may be incorrect on the ill-formed part, let me reread more carefully. – Shafik Yaghmour Nov 02 '13 at 02:07
  • @aaronman Okay it seems like it is not clear [what a compiler is required to do with an ill-formed program](http://stackoverflow.com/questions/15805394/what-is-the-c-compiler-required-to-do-with-ill-formed-programs-according-to-th) so it probably means either behavior is standards conforming, not very comforting. – Shafik Yaghmour Nov 02 '13 at 02:15
  • @ShafikYaghmour while this case doesn't apply, I would guess in some cases it's difficult or impossible to detect an ill formed program so the standard has to be lenient – aaronman Nov 02 '13 at 02:50
  • It's very clear what a conforming compiler must do with an ill-formed program. It must diagnose the error, which means issue a warning or error. The reason that conforming compilers aren't required to reject ill-formed programs is so that they can implement extensions while remaining conformant -- just so long as the extensions are designed such that any program using them is ill-formed per the standard. – Steve Jessop Nov 02 '13 at 03:19
5

It looks like:

bool b = {2} ;

is indeed a narrowing conversion if we look at the draft C++ standard section 8.5.4 List-initialization paragraph 7 says:

A narrowing conversion is an implicit conversion

and include the following bullet (emphasis mine):

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 whose value after integral promotions will fit into the target type.

bool can not represent the value 2 so this is a narrowing conversion in the strictest sense. Which makes sense the whole point of {} initialization is to prevent implicit conversions and increase type safety. Which the old style that you refer to here relies on:

bool b = 2

which depends on the section 4.12 Boolean conversions which says:

[...] A zero value, null pointer value, or null member pointer value is converted to false; any other value is converted to true. [...]

Of course the whole premise that {2} would be a narrowing conversion relies the assumption that the value of true and false is 1 and 0, which as far as I can tell is not guaranteed in the standard. Although it is implied the only thing the standard promises in conversions but if we are going to use a literal we don't need to rely on this assumption we have two perfectly good boolean literals for this true and false which is what you should be using.

For completeness sake, this narrowing conversion is ill-formed, it requires a diagnostic, so either a warning or an error is acceptable. if we look at paragraph 3, it says:

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

and include the following bullet (emphasis mine):

Otherwise, if the initializer list has a single element of type E and either T is not a reference type or its referenced type is reference-related to E, 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.

and includes the following example:

[ Example:
  int x1 {2}; // OK
  int x2 {2.0}; // error: narrowing
—end example ]
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740