Why does `bool b = 2` work well but `bool b = {2}` yield a warning of narrowing conversion?
Asked Answered
P

2

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.

Passmore answered 2/11, 2013 at 1:16 Comment(1)
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.Cinematograph
S
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

Shaeffer answered 2/11, 2013 at 1:19 Comment(16)
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.)Tallyho
@AndreyT it is an error on clang at least, on gcc haven't checkedShaeffer
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.Tallyho
I compiled the code using g++ -std=c++11 and it gave me a warning instead of an errorPassmore
@TianZhou should be an error, are you sure the compiler isn't ignoring the -std=c++11 optionShaeffer
@TianZhou it would say it, what is your compiler versionShaeffer
@AndreyT Oh I just added that option and the warning became an error!Passmore
@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.Tallyho
@AndreyT fair enough, fixedShaeffer
@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 tooShaeffer
@ShafikYaghmour clang produces an error my gcc isn't up to date enough to errorShaeffer
@ShafikYaghmour I'll amend my answerShaeffer
@Shaeffer I may be incorrect on the ill-formed part, let me reread more carefully.Cinematograph
@Shaeffer Okay it seems like it is not clear what a compiler is required to do with an ill-formed program so it probably means either behavior is standards conforming, not very comforting.Cinematograph
@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 lenientShaeffer
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.Timecard
C
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 ]
Cinematograph answered 2/11, 2013 at 1:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.