Reputation: 109119
While arguing with someone over the suggestion he made in the comment thread of this answer, I came across some code that gcc4.8 and VS2013 refuse to compile but clang happily accepts it and displays the correct result.
#include <iostream>
int main()
{
int i{ 5 };
void* v = &i;
std::cout << reinterpret_cast<int&>(*v) << std::endl;
}
Live demo. Both GCC and VC fail with the error I was expecting, complaining that the code attempts to dereference a void*
within the reinterpret_cast
. So I decided to look this up in the standard. From N3797, §5.2.10/11 [expr.reinterpret.cast]
A glvalue expression of type
T1
can be cast to the type “reference toT2
” if an expression of type “pointer toT1
” can be explicitly converted to the type “pointer toT2
” using areinterpret_cast
. The result refers to the same object as the source glvalue, but with the specified type. [ Note: That is, for lvalues, a reference castreinterpret_cast<T&>(x)
has the same effect as the conversion*reinterpret_cast<T*>(&x)
with the built-in&
and*
operators (and similarly forreinterpret_cast<T&&>(x)
). —end note ] No temporary is created, no copy is made, and constructors (12.1) or conversion functions (12.3) are not called.
In this case T1
is void
and T2
is int
, and a void*
can be converted to int*
using reinterpret_cast
. So all requirements are met.
According to the note, reinterpret_cast<int&>(*v)
has the same effect as *reinterpret_cast<int*>(&(*v))
, which, by my reckoning, is the same as *reinterpret_cast<int*>(v)
.
So is this a GCC and VC bug, or are clang and I misinterpreting this somehow?
Upvotes: 3
Views: 1119
Reputation: 206689
Thing is, the expression inside the cast is invalid. According to §5.3.1 Unary Operators:
The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points. If the type of the expression is “pointer to T,” the type of the result is “T.” [ Note: a pointer to an incomplete type (other than cv void) can be dereferenced. The lvalue thus obtained can be used in limited ways (to initialize a reference, for example); this lvalue must not be converted to a prvalue, see 4.1. — end note ]
void
isn't an object type. So things stop there, the whole expression is invalid. clang appears to get this wrong here.
What's more, the void
type can only be used in a specific set of circumstances as per §3.9.1 Fundamental types:
An expression of type void shall be used only as an expression statement (6.2), as an operand of a comma expression (5.18), as a second or third operand of ?: (5.16), as the operand of typeid or decltype, as the expression in a return statement (6.6.3) for a function with the return type void, or as the operand of an explicit conversion to type cv void.
So even if the derefence was legal, you can't use a void
"object" as the source in a cast (except a cast to void).
reinterpret_cast<int&>(*v)
might indeed have the same effect as *reinterpret_cast<int*>(&(*v))
when both these expressions are valid (and no fancy operator overloading ruins the day). Just because an expression could be re-written in a valid form doesn't imply that it is itself valid.
Upvotes: 3
Reputation: 145249
An expression of type void
is allowed as a mostly just syntactical device in a return
statement, and also you can cast an expression to void
, but that's all: there are no glvalues of type void
, an expression of type void
does not refer to memory. Thus the quoted passage from the standard, starting with a glvalue, does not apply. Thus, clang is wrong.
Upvotes: 3
Reputation: 182763
If v
is of type void *
, then *v
just makes no sense. The problem is not the cast, the problem is that it is illegal to dereference a pointer to void.
Upvotes: 1