使用reinterpret_cast将函数转换为void *,为什么它不合法?

时间:2014-12-01 13:32:15

标签: c++ pointers c++11 language-lawyer

这是我上一个问题The address of a function matching a bool vs const void* overload的切线跟进。回答者解释说:

  

[C ++ 11]标准没有定义来自a的任何标准转换   "指向功能的指针"指向void的指针。"

     

很难提供某些内容的缺席的引用,但是   我最接近的是C ++ 11 4.10 / 2 [conv.ptr]:

     
    

“指向 cv T指针”的prvalue,“其中T是对象类型,可以转换为prvalue输入“指向 cv 的指针     void”。将“指向 cv T”的指针转换为a的结果     “指向 cv void的指针”指向存储位置的开头     类型为T的对象所在的位置,就好像该对象最多     类型为T的派生对象(1.8)(即不是基类子对象)。     空指针值转换为空指针值     目的地类型。

  
     

(强调我的)

假设func被声明为void func();,如果您进行C风格演员,即(void*) func,演员表将成功。 static_cast<void*>(func)但无效,但reinterpret_cast<void*>(func)会成功。但是,您无法将后续转换的指针转换回其原始类型。例如,

精细:

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);
}

不太好:

void func() { }

int main() {
  void* s = reinterpret_cast<void*>(func);
  reinterpret_cast<decltype(func)>(s);
}

N3337首先说,

[expr.reinterpret.cast]

  

表达式reinterpret_cast<T>(v)的结果是结果   将表达式v转换为T类型。如果T是左值   引用类型或函数类型的右值引用,结果是   左值;如果T是对象类型的右值引用,则结果为   一个x值;否则,结果是一个prvalue和左值到右值   (4.1),数组到指针(4.2)和函数到指针(4.3)标准   转换是在表达式v。可以进行的转换上执行的   下面列出了使用reinterpret_cast明确执行的操作。没有   可以使用reinterpret_cast明确执行其他转换。

我粗犷的语言,我认为这是关键。最后一部分似乎暗示如果没有列出转换,那么它就是非法的。简而言之,允许的转换是:

  • 指针可以显式转换为足以容纳它的任何整数类型。
  • 可以将整数类型或枚举类型的值显式转换为指针。
  • 函数指针可以显式转换为不同类型的函数指针。
  • 可以将对象指针显式转换为不同类型的对象指针。
  • 有条件地支持将函数指针转换为对象指针类型,反之亦然。
  • 空指针值(4.10)将转换为目标类型的空指针值。
  • 类型为#34的prvalue;指向X&#34类型的T1成员的指针;可以显式转换为不同类型的prvalue&#34;指向T2&#34;类型Y的成员的指针如果T1T 2都是函数类型或两种对象类型。
  • 类型T1的左值表达式可以转换为类型&#34;引用T2&#34;如果表达式为&#34;指针T1&#34;可以显式转换为类型&#34;指针T2&#34;使用reinterpret_cast。

void*不是函数指针,对象没有函数或void类型。

[basic.types]

  

对象类型是(可能是cv限定的)类型,不是a   函数类型,不是引用类型,也不是void类型。

所以也许我正在抓稻草,但似乎reinterpret_cast<void*>(func)是非法的。但是,另一方面,[expr.static.cast] / 5表示&#34;否则,static_cast将执行下面列出的转换之一。不得进行其他转换 使用static_cast明确执行。&#34;关键的区别是&#34;应该&#34;和&#34;可以&#34;。这足以使reinterpret_cast合法或我错过了其他什么吗?

1 个答案:

答案 0 :(得分:18)

(所有引用均来自N3337,并且相当于每个草稿,直到N4296为止,即此答案至少对C ++ 11和C ++ 14 有效,但对C +无效+03 因为这个答案的第一个引用不存在。)

[expr.reinterpret.cast] / 8:

  

将函数指针转换为对象指针类型,反之亦然   有条件支持。这种转换的意义是   实现定义,但实现支持   双向转换,将一种类型的prvalue转换为   另一种类型和背面,可能具有不同的cv资格,   应产生原始指针值。

这包含在您的商家信息中。你认为void不是对象类型,但你没有考虑关键[basic.compound] / 3:

  

指向void的指针的类型或指向对象类型的指针称为对象指针类型 。< / p>

(也就是说,对象指针类型不一定是&#34;指向对象类型&#34的指针; - 标准术语让你在那里。)

的唯一原因
f = reinterpret_cast<decltype(f)>(s);

对于GCC或者Clang来说,目标类型与源表达式相反,已经衰减了 - 并且您显然不能将void*强制转换为功能类型。您需要使目标类型成为函数的指针then it works