Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 621
tianz Avatar asked Nov 02 '13 01:11

tianz


People also ask

Is widening conversion always safe?

Widening conversions (promotion) In a widening conversion, a value in a smaller variable is assigned to a larger variable with no loss of data. Because widening conversions are always safe, the compiler performs them silently and doesn't issue warnings.

What kind of type conversion is not safe?

On the other hand, the conversion in the opposite direction is known as explicit conversion. It needs a cast operator to convert higher data type into a smaller data type. This type of conversion is not type-safe and may result in loss of data.


2 Answers

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

like image 81
aaronman Avatar answered Sep 23 '22 07:09

aaronman


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 ]
like image 43
Shafik Yaghmour Avatar answered Sep 19 '22 07:09

Shafik Yaghmour