我有一个程序必须将函数存储为
void (*) (void*)
。使用该签名创建函数会导致代码重复(通常第一行是将 void 指针转换为正确的类型)并降低类型安全性(void 指针可以转换为任何内容,例如错误的类型),所以我想知道是否可以采用 void (*)(T*)
类型的函数,然后将其转换为 void(*)(void*)
并像这样调用它,类似于这样:
#include <iostream>
#include <string>
void printer(std::string* string)
{
std::cout << *string << std::endl;
}
int main()
{
//Cast the function to a one that takes a void pointer
auto func = reinterpret_cast<void(*)(void*)>(&printer);
//Create a string and call the function with it
std::string string = "Hello World";
func(&string);
return 0;
}
上面的代码编译并运行良好(在 ideone 上),但我想知道它是否符合所有标准,或者它是否是未定义的行为并且仅适用于我的特定示例和操作系统
这是未定义的行为。
[expr.call]/1:
通过函数类型与被调用函数定义的函数类型不同的表达式调用函数会导致未定义的行为。
[expr.reinterpret.cast]/6:
函数指针可以显式转换为不同类型的函数指针。除了将“指向
的指针”类型的纯右值转换为“指向T1
的指针”(其中T2
和T1
是函数类型)并返回其原始类型会产生原始指针值之外,此类指针转换的结果是未指定的。T2
C++ 根本不允许太多函数转换工作,即使对于您可能认为安全的事情(例如更改
const
细节)也是如此。当您需要这样的东西时,请使用显式包装函数。非捕获 lambda 可能是一种无需命名即可编写的简单方法;或者您可以定义一个通用模板来以您需要的方式包装其他函数。
自 C++17 起,允许的一种情况是将指向非抛出函数的指针(例如标记为
noexcept
)转换为潜在抛出函数指针类型(没有异常规范或 noexcept(false)
),然后通过潜在抛出函数类型调用它。这种函数指针转换是隐式允许的,因此甚至不需要 reinterpret_cast
或 static_cast
或其他。
这样做的行为是未定义的。标准(草案)说:
[expr.reinterpret.cast] 函数指针可以显式转换为不同类型的函数指针。 [注:效果 通过指向与函数中使用的类型不同的函数类型(9.2.3.5)的指针来调用函数 函数的定义未定义。 — 尾注] 除了转换“指向 T1 的指针”类型的纯右值 到类型“指向 T2 的指针”(其中 T1 和 T2 是函数类型)并返回到其原始类型,得到 原始指针值,此类指针转换的结果是未指定的。 [注:更多信息另见 7.3.11 指针转换的详细信息。 — 尾注]
你可以使用 lambda:
void(*func)(void*) = [](void *string) {
printer(static_cast<std::string*>(string));
};