Why can reinterpret_cast not convert an int to int? - c++

My compiler is the latest VC++ 2013 RC.
void f()
{
int n1 = 0;
int n2 = reinterpret_cast<int>(n1); // error C2440
}
error C2440: 'reinterpret_cast' : cannot convert from 'int' to 'int'
Why can reinterpret_cast not be used in such an obvious case?

According to cppreference.com the following conversion is available only since C++11:
An expression of integral, enumeration, pointer, or pointer-to-member
type can be converted to its own type. The resulting value is the same
as the value of expression.
which may not be implemented in Visual Studio 2013 RC yet.

The C++ standard says (5.2.10.2) (emphasis mine):
The reinterpret_cast operator shall not cast away constness (5.2.11). An expression of integral, enumeration, pointer, or pointer-to-member type can be explicitly converted to its own type; such a cast yields the
value of its operand.
So I'd say it's a bug.

Related

Can nullptr be converted to uintptr_t? Different compilers disagree

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.
All are standard conformant (ref. draft n4659 for C++).
nullptr is defined in [lex.nullptr] as:
The pointer literal is the keyword nullptr. It is a prvalue of type std::nullptr_t. [ Note: ..., a prvalue of this type is
a null pointer constant and can be converted to a null pointer value or null member pointer value.]
Even if notes are non normative, this one makes clear that for the standard, nullptr is expected to be converted to a null pointer value.
We later find in [conv.ptr]:
A null pointer constant is an integer literal with value zero or a prvalue of type std::nullptr_t. A
null pointer constant can be converted to a pointer type; .... A null pointer constant of integral type can
be converted to a prvalue of type std::nullptr_t.
Here again what is required by the standard is that 0 can be converted to a std::nullptr_t and that nullptr can be converted to any pointer type.
My reading is that the standard has no requirement on whether nullptr can be directly converted to an integral type or not. From that point on:
MSVC has a strict reading and forbid the conversion
Clang and gcc behaves as if an intermediary void * conversion was involved.

reinterpret_cast from rvalue to rvalue reference

Just a simple does it compile test.
gcc accepts the following while clang and msvc reject it: https://godbolt.org/z/DlUasL
float test()
{
return reinterpret_cast<float&&>(0x7F800000);
}
Which one is right according to the standard?
The conversion this reinterpret_cast expression seeks to perform is not among the list of conversions [expr.reinterpret.cast] that a reinterpret_cast can perform [expr.reinterpret.cast]/1. 0x7F800000 is a literal of integral type. The only conversion reinterpret_cast could perform that converts from a value of integral type to some other type is that of turning such a value into a pointer type [expr.reinterpret.cast]/5. float&& is a reference type, not a pointer type. The only conversion reinterpret_cast can perform that converts to a reference type is that of converting a glvalue expression [expr.reinterpret.cast]/11. 0x7F800000 is not a glvalue. Thus, this code is ill-formed. The fact that GCC would accept this is quite surprising to me and, I would say, definitely a bug that should be reported…

How to convert a pointer of type void(*)() to void*

How to convert a pointer of type void(*)() to void *?
is any of the following operators can be used to do so?
const_cast
static_cast
dynamic_cast
reinterpret_cast
Example: all of the following are compiled (tried in Visual Studio 2017), I wonder if they all have the same result after converting.
void operation(void(*callback)()) {
void* test1 = callback;
void* test2 = static_cast<void*>(callback);
void* test3 = reinterpret_cast<void*>(callback);
}
In ISO standard C++, there is no implicit conversion from any function pointer type to any object pointer type. So this line is ill-formed.
void* test1 = callback;
For the compiler to accept this without a diagnostic is a bug1 (you do have warnings enabled, correct?)
static_cast between any object pointer type and a function pointer type (either direction) is prohibited in standard C++. The controlling rule is found in 5.2.9:
Otherwise, the static_cast shall perform one of the conversions listed below. No other conversion shall be performed explicitly using a static_cast.
Since none of the rules above or below discuss function pointer casts, this forbids the line
void* test2 = static_cast<void*>(callback);
However, this is not necessarily a separate compiler bug, because one of the rules does permit static_cast to be used to accomplish any implicit conversion.
The final line is the only correct one:
void* test3 = reinterpret_cast<void*>(callback);
The controlling rule is provided in the specification for reinterpret_cast (5.2.10)
Converting a function pointer to an object pointer type or vice versa is conditionally-supported. The meaning of such a conversion is implementation-defined, except that if an implementation supports conversions in both directions, converting a prvalue of one type to the other type and back, possibly with different cv-qualification, shall yield the original pointer value.
1 When standard-compliant mode is enabled using /Za, the Microsoft C++ compilers do correctly reject the copy-initialization and static_cast attempts:
source_file.cpp(8): error C2440: 'initializing': cannot convert from void (*)(void) to void *
source_file.cpp(8): note: Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast
or function-style cast
source_file.cpp(9): error C2440: static_cast:
cannot convert from void (*)(void) to void *
source_file.cpp(9):
note: Types pointed to are unrelated; conversion requires
reinterpret_cast, C-style cast or function-style cast
In Standard C++, since C++11, it is conditionally-supported, which means that an implementation may or may not support it, and must document whether it is supported.
For implementations that do support this cast, the appropriate cast operator to use is reinterpret_cast. The details can be found in the C++14 standard [expr.reinterpret.cast]/8.
It's an error to try and convert a function pointer to an object pointer with no cast ([conv.ptr]), or with static_cast ([expr.static.cast]/5). If your compiler permits this in standard mode and doesn't issue a diagnostic then the compiler is non-conforming.

Is converting a bool (false) to a pointer legal in C++?

I've recently stumbled unto something strange: converting a boolean to pointer works in Visual Studio 2013 and 2015 but not on GCC nor Clang (tried in 3.5).
#include <iostream>
using namespace std;
void foo(int *ptr)
{
std::cout << "foo";
}
int main()
{
foo(false);
}
Error in GCC:
main.cpp: In function 'int main()':
main.cpp:13:13: error: cannot convert 'bool' to 'int*' for argument '1' to 'void foo(int*)'
foo(false);
^
My guess is that false is converted to 0 which is equivalent to NULL. Replacing the call to foo with foo(true) causes the compilation to fail with every compiler.
So my question is: is this code supposed to compile? I fail to see the benefit of converting false to a pointer, it seems to me that it would only be the cause of bugs after misuse / refactoring etc
This should not be accepted since C++11.
See Pointer conversions (emphasis mine):
A null pointer constant (see NULL), can be converted to any pointer type, and the result is the null pointer value of that type. Such conversion (known as null pointer conversion) is allowed to convert to a cv-qualified type as a single conversion, that is, it's not considered a combination of numeric and qualifying conversions.
Note since C++11 a null pointer constant might be an integer literal with value zero (or a prvalue of type std::nullptr_t), while false is not, it's a boolean literal.
And until C++11 null pointer constant is defined as an integral constant expression rvalue of integer type that evaluates to zero, while false is fine. (GCC will give a warning for it.)
From the standard, $4.10/1 Pointer conversions [conv.ptr] (emphasis mine)
A null pointer constant is an integer literal (2.13.2) with value zero or a prvalue of type std::nullptr_t.
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 (4.4).

Is Visual Studio buggy in printing the function address?

Take the following testcase:
#include <iostream>
void foo()
{}
int main()
{
std::cout << &foo << std::endl;
}
GCC 4.1.2, GCC 4.8 and GCC 4.9 (C++03 and C++11) all give the following output when building and then compiling:
$ g++ main.cpp -o test && ./test
main.cpp: In function 'int main()':
main.cpp:8:23: warning: the address of 'void foo()' will always evaluate as 'true' [-Waddress]
std::cout << &foo << std::endl;
^
1
This is supposedly because the only viable stream insertion for the function pointer is conversion-to-bool (and a cast to void* would be required to actually get an address into the stream).
However, Microsoft Visual Studio 2012 and 2013 output a pointer address instead.
Which set of toolchains is conformant? And is the non-conformance documented anywhere?
MSVC can be made to function correctly and perform the conversion from function pointer to bool if you disable language extensions (/Za switch). If you do that, your code produces the following warnings (at /W4 on VS2013)
1>main.cpp(8): warning C4305: 'argument' : truncation from 'void (*)(void)' to 'std::_Bool'
1>main.cpp(8): warning C4800: 'void (*)(void)' : forcing value to bool 'true' or 'false' (performance warning)
and the output is 1
This behavior is documented under the Casts section
Both the C++ compiler and C compiler support these kinds of non-ANSI casts:
...
Non-ANSI casts of a function pointer to a data pointer
Sure enough, the following line compiles only with /Za disabled
void *p = &foo;
Disabling language extensions produces the error message
1>main.cpp(8): error C2440: 'initializing' : cannot convert from 'void (*)(void)' to 'void *'
1> There is no context in which this conversion is possible
At least by my reading of N3337, gcc is correct and MSVC is incorrect (unless you disable its extensions).
The path starts at §4 of the standard:
Standard conversions are implicit conversions with built-in meaning. Clause 4 enumerates the full set of such conversions.
So, the only standard conversions that exist are those listed in clause 4. Not every possible standard conversion can be applied in every situation though. Only those that fit together into a standard conversion sequence can be used. A standard conversion sequence is specified as follows:
— Zero or one conversion from the following set: lvalue-to-rvalue conversion, array-to-pointer conversion, and function-to-pointer conversion.
— Zero or one conversion from the following set: integral promotions, floating point promotion, integral conversions, floating point conversions, floating-integral conversions, pointer conversions, pointer to member conversions, and boolean conversions.
— Zero or one qualification conversion.
Here we're starting from a pointer to a function, so the conversions under the first bullet point can't apply. We don't need/care about a qualification conversion, so we don't care about the third bullet point either.
To convert from pointer to function to pointer to void would clearly be a pointer conversion. These come in exactly three varieties. At §4.10/1 we have pointer conversions starting from null pointer constants (which clearly doesn't apply here). §4.10/2 covers conversions starting from:
A prvalue of type "pointer to cv T" where T is an object type [...]
That clearly doesn't apply here either, because a function isn't an object. The third option is:
A prvalue of type “pointer to cv D”, where D is a class type [...]
Again, a function isn't a class type, so that can't apply either.
That leaves us with only one option: a single conversion directly from "pointer to function" to "Boolean". That, of course, is a Boolean conversions. §4.12 says:
A prvalue of arithmetic, unscoped enumeration, pointer, or pointer to member type can be converted to a prvalue of type bool.
So, our value can be converted to a Boolean if and only if 1) it's a prvalue, and 2) it's a pointer. That probably seems pretty obvious, but if we want to confirm, we can look to the definition of the address-of operator at §5.3.1/2 and 5.3.1/3:
The result of each of the following unary operators is a prvalue.
That fulfills the first requirement.
The result of the unary & operator is a pointer to its operand. The operand shall be an lvalue or a qualified-id. If the operand is a qualified-id naming a non-static member m of some class C with type T, the result has type “pointer to member of class C of type T” and is a prvalue designating C::m. Otherwise, if the type of the expression is T, the result has type “pointer to T” and is a prvalue that is the address of the designated object (1.7) or a pointer to the designated function. [emphasis added]
That clearly fulfills the second requirement--the result is a pointer.
Since those requirements have been met, the conversion can/will happen. The result of the conversion is as follows (back to §4.12):
A zero value, null pointer value, or null member pointer value is converted to false;
any other value is converted to true.
Since we started with a pointer to an actual function, we can't have a null pointer. That leaves only one possibility: "any other value is converted to true."
Precisely as the warning from gcc said, the only possible result of the conversion is a Boolean with the value true. That will print out as "1" by default, or "true" if boolalpha has been set to true.