Reputation:
This is a tangential follow up to my previous question The address of a function matching a bool vs const void* overload. The answerer explained:
The [C++11] standard does not define any standard conversions from a "pointer to function" to a "pointer to
void
."It's hard to provide a quote for the absence of something, but the closest I can do is C++11 4.10/2 [conv.ptr]:
A prvalue of type “pointer to cv
T
,” whereT
is an object type, can be converted to a prvalue of type “pointer to cvvoid
”. The result of converting a “pointer to cvT
” to a “pointer to cvvoid
” points to the start of the storage location where the object of typeT
resides, as if the object is a most derived object (1.8) of type T (that is, not a base class subobject). The null pointer value is converted to the null pointer value of the destination type.(emphasis mine)
Assuming func
is declared void func();
, if you do a C-style cast, i.e. (void*) func
, the cast will be successful. static_cast<void*>(func)
however is invalid, but reinterpret_cast<void*>(func)
will be successful. What you cannot do however is convert the subsequently converted pointer back to its original type. For example,
Fine:
int main() {
int* i;
void* s = static_cast<void*>(i);
i = static_cast<int*>(s);
s = reinterpret_cast<void*>(i);
i = reinterpret_cast<int*>(s);
}
Not fine:
void func() { }
int main() {
void* s = reinterpret_cast<void*>(func);
reinterpret_cast<decltype(func)>(s);
}
N3337 starts off by saying,
[expr.reinterpret.cast]
The result of the expression
reinterpret_cast<T>(v)
is the result of converting the expressionv
to typeT
. IfT
is an lvalue reference type or an rvalue reference to function type, the result is an lvalue; ifT
is an rvalue reference to object type, the result is an xvalue; otherwise, the result is a prvalue and the lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) standard conversions are performed on the expression v. Conversions that can be performed explicitly usingreinterpret_cast
are listed below. No other conversion can be performed explicitly usingreinterpret_cast
.
I bolded the language that I believe is key here. The last part seems to imply that if the conversion is not listed, it's illegal. In brief summary, the allowed conversions are:
X
of type T1
" can be explicitly converted to a prvalue of a different type "pointer to member of Y of type T2
" if T1
and T
2 are both function types or both object types.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. void*
is not a function pointer and objects don't have function or void type.
[basic.types]
An object type is a (possibly cv-qualified) type that is not a function type, not a reference type, and not a void type.
So maybe I'm grasping at straws, but it seems reinterpret_cast<void*>(func)
is illegal. However, on the other hand, [expr.static.cast]/5 says "Otherwise, the static_cast
shall perform one of the conversions listed below. No other conversion shall be
performed explicitly using a static_cast
." the key difference being "shall" and "can". Is this enough to make the reinterpret_cast
legal or am I missing something else?
Upvotes: 18
Views: 5144
Reputation: 60979
(All quotes are from N3337 and are equivalent for every single draft until N4296 from there on, i.e. this answer is valid at least for C++11 and C++14 but not for C++03 as the first quote of this answer does not exist in there.)
[expr.reinterpret.cast]/8:
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 orignal pointer value.
This is contained in your listing. You argue that void
is not an object type, but you didn't consider the crucial [basic.compound]/3:
The type of a pointer to
void
or a pointer to an object type is called an object pointer type.
(That is, a object pointer type is not necessarily a "pointer to object type" - standard terminology got you there.)
The only reason that
f = reinterpret_cast<decltype(f)>(s);
Isn't fine on GCC or Clang is that the target type, as opposed to the source expression, is not decayed - and you can clearly not cast void*
to a function type. You need to make the target type a pointer to function, then it works.
Upvotes: 20