我有一个程序必须将功能存储为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上),但我想知道它是否符合所有标准,或者是否是未定义的行为,并且对于我的特定示例和操作系统都可以正常工作
答案 0 :(得分:3)
这是未定义的行为。
[expr.call] / 1:
通过表达式调用函数,该表达式的函数类型不同于被调用函数定义的函数类型,将导致未定义的行为。
[expr.reinterpret.cast] / 6:
可以将功能指针显式转换为其他类型的功能指针。除了将类型为“将指针指向
T1
”的prvalue转换为类型“将指针指向T2
”(其中T1
和T2
是函数类型)并将其返回原始值之外类型产生原始指针值,这种指针转换的结果未指定。
C ++根本不允许很多函数强制转换,即使对于您可能认为安全的事情,例如更改const
细节也是如此。当您需要这种东西时,请改用显式包装函数。不捕获lambda可能是一种无需命名的简单方法。或者您可以定义一个通用模板,以您需要的方式包装其他功能。
[奇怪的是,C ++ 17允许从非抛出函数的指针到潜在抛出函数的指针的隐式转换,即使C ++ 17做出了不同的函数类型,但是我却没有任何措辞看到说您被允许通过该转换后的指针实际调用该函数。]
答案 1 :(得分:2)
这样做的行为是不确定的。标准(草稿)说:
[expr.reinterpret.cast]函数指针可以显式转换为其他类型的函数指针。 [注意: 通过指向函数类型(9.2.3.5)的指针调用函数,该函数类型与 该函数的定义未定义。 —尾注] ,除了将类型为“ pointer to T1”的prvalue转换为 指向“指向T2的指针”的类型(其中T1和T2是函数类型),然后返回其原始类型将产生 原始指针值,则未指定此类指针转换的结果。 [注意:另请参阅7.3.11 指针转换的详细信息。 —尾注]
您可以使用lambda:
void(*func)(void*) = [](void *string) {
printer(static_cast<std::string*>(string));
};