Narrowing conversions in C++11: what is the "actual value after conversion"?
c++, c++11, language-lawyer
Solution
This is a defect in the standard, see CWG issue 1449. The text has been changed to
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
Note: the issue's status, DRWP, means that officially, the standard has not yet been changed, and an argument can be made that at least your `int64_t` example is legal in C++11. Compilers already implement the new rules, though, as this was already the intended meaning of the original wording.
Problem
Is the following code legal in C++11? ``` int16_t x {0xaabb}; int64_t xxxx {0xaaaabbbbccccdddd}; ``` The code is from "The C++ Programming Language" 4th edition (page 150). As we know, narrowing conversion is not allowed for list initialization, and among the standard's definition of narrowing conversion, we have: A narrowing conversion is an implicit conversion — [...] — 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. Checking the rule of narrowing conversion against the sample code, I justify that the sample code is illegal because `0xaabb` and `0xaaaabbbbccccdddd` cannot be represented in `int16_t` and `int64_t` respectively. Is that correct? But I don't quite understand the wording "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". I wonder in what scenario the actual value after conversion cannot fit into the target type. Since conversions between integer types are always valid (though implementation-defined in the case where the destination type is signed and the source value cannot be represented in the destination type, but it's not undefined behaviour anyway), is it always true that "the value after conversion will fit into the target type"? And from this point of view, I'm starting to question my judgement of the sample code being narrowing conversion. If that is the case, why does the standard put something always true in a condition? Why not just say "except where the source is a constant expression and the actual value after conversion will produce the original value when converted back to the original type"? Can someone help me clarify this? Thank you!