[mpl] clang error for narrowing from unsigned int 0xffffffff to long

In the mpl bitwise.cpp test there are lines like: MPL_ASSERT_RELATION( (bitor_<_0,_ffffffff>::value), ==, 0xffffffff ); MPL_ASSERT_RELATION( (bitor_<_1,_ffffffff>::value), ==, 0xffffffff ); MPL_ASSERT_RELATION( (bitor_<_8,_ffffffff>::value), ==, 0xffffffff ); Using clang under Windows with the VC++ RTL ( clang-cl ), clang gives an error of: bitwise.cpp(43,25) : error: non-type template argument evaluates to 4294967295, which cannot be narrowed to type 'long' [-Wc++11-narrowing] MPL_ASSERT_RELATION( (bitor_<_0,_ffffffff>::value), ==, 0xffffffff ); for the bitor_<_0,_ffffffff>::value expression part since the assert_relation template takes two 'long's as its values. Is this an actual bug according to the C++11 standard ? I would have thought that an unsigned int value of 0xffffffff would fit into a long as a -1, but it sounds like the new standard is saying that since the value of 4294967295 cannot be encompassed by a long it is an error. if it is an error how should it be fixed in MPL ?

On 13 November 2013 20:46, Edward Diener wrote:
Yes.
I would have thought that an unsigned int value of 0xffffffff would fit into a long as a -1,
It's not a matter of fitting into a given number of bytes, the value would not be the same, so it's not a value-preserving conversion.
Yes, in certain contexts where narrowing conversions are forbidden, notably as non-type template parameters or inside braced initialization lists.
if it is an error how should it be fixed in MPL ?
You can use an explicit conversion, e.g. (long)0xffffffff

On 11/13/2013 5:37 PM, Jonathan Wakely wrote:
Or my own preference of 'static_cast<long>(0xffffffff)'. But using '0xffffffffL' does not work. I admit that understanding the C++ Standard's different interprtetation of these two forms is beyond me.

On 14/11/2013 17:22, Quoth Edward Diener:
0xffffffffL is an invalid literal value because it is out of range for the "long" type. (Using UL or even just U would make it valid, but still not the type you wanted.) 0xffffffff does not explicitly specify a size so the compiler is allowed to pick one itself; it will probably infer "unsigned int", as "signed int" is too small. (Or on platforms where "int" is 16-bit, it would infer "unsigned long".) An explicit cast on an integer is allowed to reinterpret unsigned to/from signed and/or extend or truncate bits, through long historic usage in C code. (Technically that usage should ideally require reinterpret_cast instead of static_cast, since you're reinterpreting the sign bit. But again I think tradition won out here -- and it's a less scary conversion than other places you need reinterpret_cast.)

On 14 November 2013 05:17, Gavin Lambert wrote:
It's not invalid, it has the value 4294967295 and a type according to Table 6 in 2.14.2 [lex.icon]. For 32-bit targets it has type unsigned long because 4294967295 doesn't fit in a 32-bit long, and when converted to long it is still a narrowing conversion because the value changes.
The compiler doesn't get to pick, again Table 6 says what its type is.
And a type cast is not a context where narrowing conversions are forbidden. The point of introducing narrowing conversions into C++11 was to close some of the holes in the C types system that allow silent, value-changing conversions, but for backwards compatibility to only prevent them in some contexts, e.g. using the new C++11 braced-init syntax or in "converted constant expressions" (as used for non-type template arguments of integral type, see 14.3.2 [temp.arg.nontype] p5.)
No, reinterpret_cast between different integer types is ill-formed. Only the conversions listed in [expr.reinterpret.cast] are allowed.

On 14/11/2013 23:02, Quoth Jonathan Wakely:
That counts as the compiler picking the type, because on different platforms a different result occurs, and it has a wider range to choose from without the suffix. As I said, on 16-bit platforms it would pick "unsigned long" and on 32-bit platforms it would pick "unsigned int".
I know that, that wasn't my point. I wasn't saying that reinterpret_cast is valid according to the standard, I was saying that the internalised meaning of reinterpret_cast in most people's heads ("interpret the underlying raw bit storage of this variable as this other type") is closer in behaviour to what static_cast on a same-size integer type actually does (at least in most implementations), vs. what static_cast does when performing any other conversion (eg. integer to float alters the bit pattern, pointer to other pointer might return a different pointer value, etc). I'm not even saying that this is wrong -- part of the reason to make the cast types explicit over the C-style casts was to more clearly call out the places where you're doing "scary" things (aka reinterpret_cast), to enhance type safety and stamp out void* -- and integer conversions aren't sufficiently scary (although arguably they're still something you need to pay almost as much attention to -- while they're less likely to crash or UB if used wrong than bad pointer casts, they can still produce incorrect results). (And I might be wrong about this [as I haven't looked at C++11 changes in detail] but IIRC the standard does not actually guarantee that ints are in two's-complement form, so the integer static_cast might not be reversible, although I think a lot of code assumes it is.)

On 11/14/2013 6:41 PM, Gavin Lambert wrote:
I think that you are right that the standard says nothing about two's complement notation. It does seem a pretty safe bet that the cast is reversible between 'unsigned int' and 'long' since the 'long' is guaranteed to be at least the size in bits of the 'unsigned int'.

On 14 November 2013 04:22, Edward Diener wrote:
Yep, that's equivalent, and arguably clearer and cleaner.
'0xffffffffL' does not work. I admit that understanding the C++ Standard's different interprtetation of these two forms is beyond me.
The expression static_cast<long>(0xffffffff) has type long, the expression 0xffffffff has a type given by [lex.icon], which on a 32-bit system will be unsigned long. Using that expression as a non-type template argument (14.3.2 [temp.arg.nontype] p5) requires no conversion for the former, but requires an implicit conversion from unsigned long to long in the latter case, and since the value would change that is a narrowing conversion, which is not allowed in a converted constant expression (5.19 [expr.const] p3).

On 11/14/2013 5:09 AM, Jonathan Wakely wrote:
Thanks for the full explanation. I solved the issue with the MPL bitwise.cpp test by doing the appropriate static_cast<long>(expression), tested it with gcc, clang, VC++, and updated it on 'trunk'.
participants (3)
-
Edward Diener
-
Gavin Lambert
-
Jonathan Wakely