This is purely a philosophical question. I assume that there's no reasonable context in which the result will prove to be useful (given nullptr
).
According to this - https://en.cppreference.com/w/cpp/language/integer_literal, the type of integral literals is either int
, long int
, long long int
, unsigned int
, unsigned long int
or unsigned long long int
, with possible implementation-specific exceptions if the value of the literal doesn't fit into any of the above.
None of these types are convertible to void *
, unless the value of the literal is 0.
Different compilers handle this differently. For example, consider the following conversions:
void g(void * p){}
void f(){
int i = 0;
void * p;
// p = i; // Fails. Also for other integral types.
p = 0; // Works. Also for 00, 0x0 and 0b0. Also when adding `u` and `l` suffixes.
g(0); // Also works.
// g(1); // Fails.
// Amazingly, even this seems to work with gcc, icc and msvc, but not with clang:
void * x = static_cast<int>(0);
// These works for icc and msvc, but fails with gcc and clang
p = static_cast<int>(0);
g(static_cast<int>(0));
}
What happens "under the hood" that enables compilers to perform these int
->void *
conversions?
Edit: Specifically, the question is what does the standard has to say about this?
The question is, why is this permitted according to the standard
Because there needs to be a way to express null pointer. The designer of the C language chose that 0 would be null. The designer of C++ chose to be compatible with C, and as such 0 is a null pointer constant.
Later in C++11, nullptr
was introduced as a new keyword. The integral null pointer constants cannot be replaced because that would break backward compatibility, so these different ways to express null co-exist. There is no reason to use 0 as null pointer if you don't need to support pre-C++11 systems.
and specifically what is permitted
Standard says (latest draft):
[conv.ptr] A null pointer constant is an integer literal ([lex.icon]) with value zero or a prvalue of type std::nullptr_t. A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type ([basic.compound]) and is distinguishable from every other value of object pointer or function pointer type. Such a conversion is called a null pointer conversion. Two null pointer values of the same type shall compare equal. The conversion of a null pointer constant to a pointer to cv-qualified type is a single conversion, and not the sequence of a pointer conversion followed by a qualification conversion ([conv.qual]). A null pointer constant of integral type can be converted to a prvalue of type std::nullptr_t. [ Note: The resulting prvalue is not a null pointer value. — end note ]
What happens "under the hood" that enables compilers to perform these int->void * conversions?
The compiler parses the source. The grammar says that 0 is a literal. The compiler treats is as a literal 0, and as such lets it be converted to any pointer type as per standard.
// Amazingly, even this seems to work with gcc, icc and msvc, but not with clang:
void * x = static_cast<int>(0);
This is ill-formed since C++11. When an ill-formed program compiles, it is typically either because
In this case, it is probably a language extension.
// These works for icc and msvc, but fails with gcc and clang
p = static_cast<int>(0);
g(static_cast<int>(0));
These are also ill-formed since C++11. I don't know enough about icc and msvc to tell you whether these cases are intentional. I recommend checking their documentation regarding that.
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