Consider this program:
#include <cstdint>
using my_time_t = uintptr_t;
int main() {
const my_time_t t = my_time_t(nullptr);
}
It failed to compile with msvc v19.24:
<source>(5): error C2440: '<function-style-cast>': cannot convert from 'nullptr' to 'my_time_t'
<source>(5): note: A native nullptr can only be converted to bool or, using reinterpret_cast, to an integral type
<source>(5): error C2789: 't': an object of const-qualified type must be initialized
<source>(5): note: see declaration of 't'
Compiler returned: 2
but clang (9.0.1) and gcc (9.2.1) "eat" this code without any errors.
I like the MSVC behaviour, but is it confirmed by standard? In other words is it bug in clang/gcc or it is possible to interpret standard that this is right behaviour from gcc/clang?
In my opinion MSVC is not behaving standard-conform.
I am basing this answer on C++17 (draft N4659), but C++14 and C++11 have equivalent wording.
my_time_t(nullptr)
is a postfix-expression and because my_time_t
is a type and (nullptr)
is a single expression in a parenthesized initializer list, it is exactly equivalent to an explicit cast expression. ([expr.type.conv]/2)
The explicit cast tries a few different specific C++ casts (with extensions), in particular also reinterpret_cast
. ([expr.cast]/4.4) The casts tried before reinterpret_cast
are const_cast
and static_cast
(with extensions and also in combination), but none of these can cast std::nullptr_t
to an integral type.
But reinterpret_cast<my_time_t>(nullptr)
should succeed because [expr.reinterpret.cast]/4 says that a value of type std::nullptr_t
can be converted to an integral type as if by reinterpret_cast<my_time_t>((void*)0)
, which is possible because my_time_t = std::uintptr_t
should be a type large enough to represent all pointer values and under this condition the same standard paragraph allows the conversion of void*
to an integral type.
It is particularly strange that MSVC does allow the conversion if cast notation rather than functional notation is used:
const my_time_t t = (my_time_t)nullptr;
Although I can find no explicit mention in this Working Draft C++ Standard (from 2014) that conversion from std::nullptr_t
to an integral type is forbidden, there is also no mention that such a conversion is allowed!
However, the case of conversion from std::nullptr_t
to bool
is explicitly mentioned:
4.12 Boolean conversions
A prvalue of arithmetic, unscoped enumeration, pointer, or pointer to member type can be converted to a prvalue of type bool. A zero value, null pointer value, or null member pointer value is converted to false; any other value is converted to true. For direct-initialization (8.5), a prvalue of type std::nullptr_t can be converted to a prvalue of type bool; the resulting value is false.
Further, the only place in this draft document where conversion from std::nullptr_t
to an integral type is mentioned, is in the "reinterpret_cast" section:
5.2.10 Reinterpret cast
...
(4) A pointer can be explicitly converted to any integral type large enough to hold it. The mapping function is implementation-defined. [ Note: It is intended to be unsurprising to those who know the addressing structure of the underlying machine. — end note ] A value of type std::nullptr_t can be converted to an integral type; the conversion has the same meaning and validity as a conversion of (void*)0 to the integral type. [Note: A reinterpret_cast cannot be used to convert a value of any type to the type std::nullptr_t. — end note ]
So, from these two observations, one could (IMHO) reasonably surmise that the MSVC
compiler is correct.
EDIT: However, your use of the "functional notation cast" may actually suggest the opposite! The MSVC
compiler has no problem using a C-style cast in, for example:
uintptr_t answer = (uintptr_t)(nullptr);
but (as in your code), it complains about this:
uintptr_t answer = uintptr_t(nullptr); // error C2440: '<function-style-cast>': cannot convert from 'nullptr' to 'uintptr_t'
Yet, from the same Draft Standard:
5.2.3 Explicit type conversion (functional notation)
(1) A simple-type-specifier (7.1.6.2) or typename-specifier (14.6) followed by a parenthesized expression-list constructs a value of the specified type given the expression list. If the expression list is a single expression, the type conversion expression is equivalent (in definedness, and if defined in meaning) to the corresponding cast expression (5.4). ...
The "corresponding cast expression (5.4)" can refer to a C-style cast.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With