user3920237
user3920237

Reputation:

Using reinterpret_cast to cast a function to void*, why isn't it illegal?

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,” where T is an object type, can be converted to a prvalue of type “pointer to cv void”. The result of converting a “pointer to cv T” to a “pointer to cv void” points to the start of the storage location where the object of type T 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 expression v to type T. If T is an lvalue reference type or an rvalue reference to function type, the result is an lvalue; if T 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 using reinterpret_cast are listed below. No other conversion can be performed explicitly using reinterpret_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:

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

Answers (1)

Columbo
Columbo

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

Related Questions