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

16 c++ pointers language-lawyer c++11

这是我前一个问题切线跟进.匹配bool vs const void*overload的函数的地址.回答者解释说:

[C++ 11]标准没有定义从"指向函数的指针"到"指向"的任何标准转换void.

没有东西可以提供引用,但我能做的最接近的是C++ 11 4.10/2 [conv.ptr]:

类型为"指向cv的 指针"的prvalue T,其中T是对象类型,可以转换为类型为"指向cv的 指针"的prvalue void.一个"指针转换的结果CV T "发送给"指针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);
}
Run Code Online (Sandbox Code Playgroud)

不好:

void func() { }

int main() {
  void* s = reinterpret_cast<void*>(func);
  reinterpret_cast<decltype(func)>(s);
}
Run Code Online (Sandbox Code Playgroud)

N3337开始说,

[expr.reinterpret.cast]

表达式reinterpret_cast<T>(v)的结果是将表达式转换v为type 的结果T.如果T是左值引用类型或函数类型的右值引用,则结果为左值; if T是对象类型的右值引用,结果是xvalue; 否则,结果是一个prvalue,并且对表达式v执行左值到右值(4.1),数组到指针(4.2)和函数到指针(4.3)标准转换.可以执行的转换明确使用reinterpret_cast如下所列.无法使用明确执行其他转换reinterpret_cast.

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

  • 指针可以显式转换为足以容纳它的任何整数类型.
  • 可以将整数类型或枚举类型的值显式转换为指针.
  • 函数指针可以显式转换为不同类型的函数指针.
  • 可以将对象指针显式转换为不同类型的对象指针.
  • 有条件地支持将函数指针转换为对象指针类型(反之亦然).
  • 空指针值(4.10)将转换为目标类型的空指针值.
  • 甲prvalue类型的"指针的构件X类型的T1"可被明确地转换成不同类型的prvalue"指向类型Y的成员T2"如果T1T2都为函数类型或两者的对象类型.
  • 如果可以使用reinterpret_cast 将"指向" 的类型的表达式显式转换为"指向" 的类型,则T1可以将类型的左值表达式强制转换为"引用T2"类型.T1T2

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

[basic.types]

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

所以也许我正在抓住稻草,但这似乎reinterpret_cast<void*>(func)是非法的.但是,另一方面,[expr.static.cast]/5表示"否则,static_cast应执行下面列出的转换之一.不应使用a明确执行其他转换static_cast." 关键区别是"应"和"可以".这足以使reinterpret_cast合法化还是我遗漏了别的东西?

Col*_*mbo 18

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

[expr.reinterpret.cast]/8:

有条件地支持将函数指针转换为对象指针类型(反之亦然).这种转换的含义是实现定义的,除非实现支持两个方向的转换,将一种类型的prvalue转换为另一种类型并返回,可能具有不同的cv-qualification,将产生orignal指针值.

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

的类型的一个指针void或指向对象类型被称为对象的指针类型.

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

唯一的原因

f = reinterpret_cast<decltype(f)>(s);
Run Code Online (Sandbox Code Playgroud)

在GCC或Clang上是不行的是,与源表达式相反,目标类型不会衰减 - 并且您显然不能void*转换为函数类型.您需要使目标类型成为函数的指针,然后才能工作.

  • 只是为了补充这个好的答案,(1)对象和函数指针之间转换的条件支持是最近的(C++ 11或C++ 14); (2)但是这种转换的要求已经成为Posix的一部分很长一段时间,并且在Windows API中是必不可少的,所以本质上语言已经赶上现实; (3)将对象指针与函数指针分离,与哈佛架构相关联,该架构仍在使用中. (5认同)