简体   繁体   中英

reinterpret_cast behavior when dereferencing a void pointer

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 to T2 ” if an expression of type “pointer to T1 ” can be explicitly converted to the type “pointer to T2 ” using a reinterpret_cast . The result refers to the same object as the source glvalue, but with the specified type. [ Note: That is, for lvalues, a reference cast reinterpret_cast<T&>(x) has the same effect as the conversion *reinterpret_cast<T*>(&x) with the built-in & and * operators (and similarly for reinterpret_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?

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.

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM