7

Can anyone please explain to me, why the compiler allows initialize variables of built-in type if the initializer might lead to the loss of information?

For example C++ Primer, the 5th edition says, that The compiler will not let us list initialize variables of built-in type if the initializer might lead to the loss of information.

but my compiler gcc v 4.7.1 initialized variable a in the following code successfully:

long double ld = 3.1415926536; 
int a{ld};

there was just warning: narrowing conversion of ‘ld’ from ‘long double’ to ‘int’ inside { } [-Wnarrowing].

vladinkoc
  • 919
  • 1
  • 8
  • 13
  • 2
    I would say the book is wrong. C and C++ are very lax with that kind of things... – Matthieu M. Oct 13 '12 at 14:18
  • @MatthieuM. C++11 isn't. – rubenvb Oct 13 '12 at 14:37
  • 1
    **n3337** **4.9/1 [conv.fpint]** *A prvalue of a floating point type can be converted to a prvalue of an integer type. The conversion truncates; that is, the fractional part is discarded. The behavior is undefined if the truncated value cannot be represented in the destination type.* So conversion is possible and actually defined in the Standard, what is unclear is whether it's allowed or not *here*, and I did not yet found anything about that. – Matthieu M. Oct 13 '12 at 14:44
  • 2
    **n3337 8.5.4/3 [dcl.init.list]**: *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.* I don't see the ambiguity. – rubenvb Oct 13 '12 at 15:14

3 Answers3

11

One of the features of initializer lists is that narrowing conversions are not allowed. But the language definition doesn't distinguish between warnings and errors; when code is ill-formed it requires "a diagnostic", which is defined as any message from a set of implementation-defined messages. Warnings satisfy this requirements. That's the mechanism for non-standard extensions: having issued a warning, the compiler is free to do anything it wants to, including compiling something according to implementation-specific rules.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
2

You can set the compiler flag to flag all warnings as error. In that case only it will stop you from doing like that. Otherwise it will only be a warning.

Ashish Yadav
  • 557
  • 1
  • 5
  • 12
0

This issue has been coming up lately. With gcc-4.7 a command line switch turns on the required behaviour:

g++ -Werror=narrowing ...
Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • Isn't this something C++11 changes? I thought narrowing conversions were allowed pre-C++11, but forbidden in light of the generalized initialization syntax in c++11. – rubenvb Oct 13 '12 at 14:25
  • 2
    @rubenvb - it doesn't change anything from C++03; it introduces a **new syntax** for initialization, with different rules (no narrowing conversions); code that uses the other forms of initialization is not subject to the restriction on narrowing conversions. – Pete Becker Oct 13 '12 at 14:29
  • 2
    @Pete Initializing an array with a braced-init-list (which is possible in C++03 under a different name) does not allow narrowing conversions in C++11, and is a breaking change. – rubenvb Oct 13 '12 at 14:33
  • @rubenvb - I haven't been through the details, but I don't think old-style aggregate initialization is affected by the new rules. I certainly hope not... – Pete Becker Oct 13 '12 at 14:40
  • 3
    @PeteBecker [It is](http://liveworkspace.org/code/ded1e87791d51fe0f2a507826eecd801), better get used to it. I think it's great, people should use *the right types*... everywhere. – rubenvb Oct 13 '12 at 14:42
  • @rubenvb - the code you link to attempts to initialize an array of 2 `int` with three `int` values; there's no narrowing conversion involved. And the rule about narrowing conversions applies when the initializer list has "a single element". – Pete Becker Oct 13 '12 at 14:50
  • @rubenvb: you are right that all types within an initializer-list should be identical (and that this is required by the Standard), however I am still unconvinced whether this should apply here. It seems gcc 4.7.2 agrees with you, but I have yet to see the exact point being mentioned in the Standard (searching for `initializer` is quite a bore...) – Matthieu M. Oct 13 '12 at 14:51
  • @PeteBecker: have a closer look, it's an attempt to initialize from both a `int` and a `float` which is rejected. gcc 4.7.2 also rejects `int a{1.2};` by the way. As well as the example by the OP (well, at least it warns). – Matthieu M. Oct 13 '12 at 14:52
  • @MatthieuM. look for "braced-init-list", I think you'll have better luck. – rubenvb Oct 13 '12 at 14:52
  • @MatthieuM. - thanks for pointing that out. The image was blurry, and I mis-read the period as a comma. This is old-style aggregate initialization, and a compiler that rejects it is wrong. As to `int a{1.2}`, yes, that the **new** initializer form, and the rule is that narrowing conversions aren't allowed. – Pete Becker Oct 13 '12 at 14:55
  • @MatthieuM. - note also that compilers are free to warn about anything the compiler writer thinks is appropriate, even when it's valid and meaningful. – Pete Becker Oct 13 '12 at 14:56
  • @rubenvb - "people should use the right types" - that's not what the new rules require. `float a{1.2}` is okay, even though the type of `1.2` is `double`. Converting `double` to `float` is allowed if the value is in the range that can be represented by a `float`, even if the conversion is not exact. – Pete Becker Oct 13 '12 at 15:02
  • @PeteBecker: Yes, obviously, the question here is whether gcc should reject the program or not according to the Standard. I believe not according to my reading. – Matthieu M. Oct 13 '12 at 15:02
  • 2
    @Pete The standard lists the array example as a breaking change in C.2.4, and provides an example that is functionally the same as ruben's in 8.5.4/3, marked with the comment "error: narrowing". – R. Martinho Fernandes Oct 13 '12 at 15:06
  • @R.MartinhoFernandes - yes, but note that that example has **only one initializer**. That's the case that changed, not aggregate initialization in general. – Pete Becker Oct 13 '12 at 15:12
  • 1
    @Pete No, the example in 8.5.4/3 has two initializers. If you want a more definitive proof, the rules for aggregate initialization in §8.5.1/2 read "(...) If the *initializer-clause* is an expression and a narrowing conversion (8.5.4) is required to convert the expression, the program is ill-formed." – R. Martinho Fernandes Oct 13 '12 at 15:13
  • @R.MartinhoFernandes - you're right. Sorry for all the noise. But, again, it's pretty much the weird cases (like initializing an `int` from a `double`) that are prohibited, not more normal ones like initializing a `float` from a constant `double` that's in range. – Pete Becker Oct 13 '12 at 15:17
  • @PeteBecker I didn't bring that up, and that's not what I meant with "use the right types". The OP clearly uses floating point to integer conversion, which does qualify for my statement. – rubenvb Oct 13 '12 at 15:20
  • @PeteBecker also, http://stackoverflow.com/questions/4434140/narrowing-conversions-in-c0x-is-it-just-me-or-does-this-sound-like-a-breakin :) – Johannes Schaub - litb Oct 13 '12 at 18:49