9

Since switching on the C++0x standard in g++, I've started seeing 'narrowing conversion' errors, particularly when converting from an 'int' to a 'short' although I understand the error covers a much broader swath of conversions.

Can anyone shed some light on the rational for introducing this extra level of safety?, What are the possible consequences of disabling this error? (apart from the potential loss of precision).

Thanks.

Community
  • 1
  • 1
Gearoid Murphy
  • 11,834
  • 17
  • 68
  • 86
  • 43
    [Your rocket could explode](http://www.ima.umn.edu/~arnold/disasters/ariane.html) – Mike Seymour Dec 20 '11 at 16:44
  • 1
    If I ever work on a rocket, I'll be sure to keep that in mind ;) – Gearoid Murphy Dec 20 '11 at 16:48
  • 4
    This is something that has produced _warnings_ always, forever, and forever and a day, and it's a good thing. Now it's an _error_, it's debatable whether that is a good thing since it breaks compilation of existing, presumably correct, code. However, it prevents some impossible to track errors, so I can see the rationale. I wouldn't disable such an error logic, because when you accidentially throw away the upper half of a value and they have a meaning, your code still works just fine until it doesn't, and then you don't know why. – Damon Dec 20 '11 at 16:55
  • 7
    Imagine for example an API function which checks for an `(unsigned) -1` error code, and you truncate the binary value `0xffffffff` to `0x0000ffff`. Or imagine a loop that will run forever because the exit condition can never be true. Or worse, a loop that will _sometimes_ run forever, if some particular user input causes such a problem. You'll never find out why. – Damon Dec 20 '11 at 16:59
  • 2
    Damon, flesh out an answer to that effect and I'll accept it. – Gearoid Murphy Dec 20 '11 at 17:01

1 Answers1

12

From Assignment and compound assignment operators [expr.ass]

The meaning of x={v}, where T is the scalar type of the expression x, is that of x=T(v) except that no narrowing conversion (8.5.4) is allowed.

and from List-initialization [dcl.ini.list]

If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed.

So basically you can't ignore it, your program is ill-formed in presence of narrowing conversions.

From Implementation compliance:

Implementations are required to diagnose programs that use such extensions that are ill-formed according to this International Standard. Having done so, however, they can compile and execute such programs.

Bjarne Stroustroup say this:

Preventing narrowing

The problem: C and C++ implicitly truncates:

   int x = 7.3;        // Ouch!
    void f(int);
    f(7.3);         // Ouch!

However, in C++0x, {} initialization doesn't narrow:

int x0 {7.3};   // error: narrowing
int x1 = {7.3}; // error: narrowing
double d = 7;
int x2{d};      // error: narrowing (double to int)
char x3{7};     // ok: even though 7 is an int, this is not narrowing
vector<int> vi = { 1, 2.3, 4, 5.6 };    // error: double to int narrowing

The way C++0x avoids a lot of incompatibilities is by relying on the actual values of initializers (such as 7 in the example above) when it can (and not just type) when deciding what is a narrowing conversion. If a value can be represented exactly as the target type, the conversion is not narrowing.

char c1{7};      // OK: 7 is an int, but it fits in a char
char c2{77777};  // error: narrowing 

Note that floating-point to integer conversions are always considered narrowing -- even 7.0 to 7.

So in a way, narrowing also increases type safety.

Sebastian Mach
  • 38,570
  • 8
  • 95
  • 130