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:
- A pointer can be explicitly converted to any integral type large enough to hold it.
- A value of integral type or enumeration type can be explicitly converted to a pointer.
- A function pointer can be explicitly converted to a function pointer of a different type.
- An object pointer can be explicitly converted to an object pointer of a different type.
- Converting a function pointer to an object pointer type or vice versa is conditionally-supported.
- The null pointer value (4.10) is converted to the null pointer value of the destination type.
- A prvalue of type "pointer to member of
X
of typeT1
" can be explicitly converted to a prvalue of a different type "pointer to member of Y of typeT2
" ifT1
andT
2 are both function types or both object types. - An lvalue 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 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?