使用 reinterpret_cast 将函数转换为 void*,为什么它不违法?
Using reinterpret_cast to cast a function to void*, why isn't it illegal?
这是我上一个问题的切线跟进 匹配布尔值与常量 void* 重载的函数地址。回答者解释说:
[C++11] 标准未定义来自 "指向函数的指针"到"指向
void
的指针"。很难为缺少某些东西提供报价,但是 我能做的最接近的是 C++11 4.10/2 [conv.ptr]:
类型为"指向 cv 的指针
T
"的 prvalue,其中T
是对象类型,可以转换为类型为"指向 cv 的指针"的 prvaluevoid
"。将"指向 cvT
的指针"转换为 "指向 cvvoid
的指针"指向存储位置的开头 类型为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 首先说:
[重新诠释]
表达式
reinterpret_cast<T>(v)
的结果是 将表达式v
转换为类型T
。如果T
是左值 引用类型或对函数类型的右值引用,结果为 一个左值;如果T
是对对象类型的右值引用,则结果为 一个 x值;否则,结果是 prvalue 和 lvalue-to-rvalue (4.1(、数组到指针 (4.2( 和函数到指针 (4.3( 标准 转换是在表达式 v 上执行的。 可以 下面列出了使用reinterpret_cast
显式执行。不 其他转换可以使用reinterpret_cast
显式执行。
我加粗了我认为在这里很关键的语言。最后一部分似乎暗示,如果未列出转换,则这是非法的。简而言之,允许的转换是:
- 指针可以显式转换为任何足够大的整数类型来容纳它。
- 整型或枚举类型的值可以显式转换为指针。
- 函数指针可以显式转换为不同类型的函数指针。
- 对象指针可以显式转换为不同类型的对象指针。 有
- 条件地支持将函数指针转换为对象指针类型,反之亦然。
- 空指针值 (4.10( 将转换为目标类型的空指针值。
- 如果
T1
和T
2 都是函数类型或两个对象类型,则可以显式转换为不同类型的X
prvalue"指向类型T1
T2
的 Y 成员的指针"。 - 如果"指向
T1
的指针"类型的表达式可以使用reinterpret_cast显式转换为"指向T2
的指针"类型,则可以将类型T1
的左值表达式转换为类型"对T2
的引用"。
void*
不是函数指针,对象没有函数或 void 类型。
[基本类型]
对象类型是(可能符合 cv 条件的(类型,但不是 函数类型,不是引用类型,也不是 void 类型。
所以也许我抓住了稻草,但这似乎reinterpret_cast<void*>(func)
是非法的。但是,另一方面,[expr.static.cast]/5 说:"否则,static_cast
应执行下面列出的转换之一。不得进行其他转换使用static_cast
明确执行",关键区别是"应"和"可以"。这足以使reinterpret_cast
合法还是我错过了其他东西?
(所有引号均来自 N3337,并且对于从那里开始的 N4296 之前的每个草稿都是等效的,即此答案至少对 C++11 和 C++14 有效,但对 C++03 无效,因为此答案的第一个引号不存在。
[expr.reinterpret.cast]/8:
将函数指针转换为对象指针类型,反之亦然 有条件地支持。这种转换的含义是 实现定义,除非实现支持 双向转换,将一种类型的 prvalue 转换为 另一种类型和背部,可能具有不同的简历资格, 应生成原始指针值。
这包含在您的列表中。你认为void
不是一个对象类型,但你没有考虑关键的[basic.compound]/3:
指向
void
的指针类型或指向对象类型的指针的类型称为对象指针类型。
(也就是说,对象指针类型不一定是"指向对象类型的指针" - 标准术语使您到达了那里。
唯一的原因
f = reinterpret_cast<decltype(f)>(s);
在 GCC 或 Clang 上不好的是,与源表达式相反,目标类型没有衰减 - 并且您显然不能void*
转换为函数类型。您需要使目标类型成为指向函数的指针,然后它就可以工作了。
- 为什么随机数生成器不在void函数中随机化数字,而在main函数中随机化
- 为什么野牛仍在使用"int yylex(void)",却找不到"int yylex(YYS
- C++为什么尽管我调用了void函数,它却不起作用
- 为什么这个函数将"const char*"转换为"void* const"而不是"const void*"
- 为什么我在使用void函数时得到错误代码C2276
- 为什么在逗号分隔符上下文中将预增量的结果强制转换为void
- 为什么我们要为avl树实现返回一个指向节点的指针,而不是void函数
- <Windows>为什么 std::thread::native_handle 返回类型为"long long unsigned int"的值,而不是 void*(又名 HANDLE)?
- 为什么我会" void value not ignored as it ought to be"?
- 为什么在将 void 指针转换为整数指针时出现分段错误
- 为什么 c++ 中的 main() 函数不采用除 int 和 void 之外的任何其他返回类型
- 错误:为什么"void*"不是指向对象的指针类型,即使指针设置为对象?
- 为什么 void 函数不打印任何C++内容?
- 为什么 void 排序(int *[], int) 会导致"Unable to read memory"?
- 为什么 void 以这种方式与内联函数一起使用
- 为什么 void 参数不能存在于具有多个参数的函数中?
- 为什么 void* 不是迭代器类型?
- 为什么(void)sizeof(param)不"使用"参数?
- 为什么 void 在 C++ 中不取 void 值?
- 为什么void*作为模板参数可以作为函数参数,但不能作为模板参数