将函数指针转换为指向函数类型的指针的结果
(5.2.10/6)C++ 03指向函数的指针可以显式转换为指向不同类型函数的指针。通过一个指向函数类型(8.3.5)调用函数 的效果是不一样的 在函数的定义中使用的类型是未定义的。除了 该类型的右值转换“指针T1”的类型“指针 到T2”(其中,T1和T2是功能类型),并且返回到其原始 类型产生的原始指针值,这样的指针的结果 转换未指定。 [注:也见4.10为 指针转换详情]将函数指针转换为指向函数类型的指针的结果
以下是我想要做的,而很显然,转换fp1
到fp2
的结果会产生一个原始的指针,但在同一时间标准的措辞是"The result of such a pointer conversion is unspecified"
这是什么意思?
int f() { return 42; }
int main()
{
void(*fp1)() = reinterpret_cast<void(*)()>(f);
int(*fp2)() = reinterpret_cast<int(*)()>(fp1);
// Safe to call the function ?
fp2();
}
您误读标准中,“未指定”部分仅适用于其他类型的转换:
除[特例]这样的指针转换的结果是不确定的。
[特殊情况]是你转换回原来的函数指针类型,就像你的例子。在这种特殊情况下,转换会产生原始指针值,所以可以像在你的例子中那样使用它。
仅用于其他转换,结果未指定。
是的,你很安全。你正在做的是“除了转换......”情况。
的原因,这就是所谓的出来是为了让你通过另一种类型的函数指针的传递函数指针。所以,你可以定义是这样的:
enum CallbackType {
eFuncPtrVoidReturningVoid,
eFuncPtrVoidReturningInt,
// ... more as needed ...
};
class CallbackRecord
{
public:
CallbackRecord(void (*cb)()): cbType(eFuncPtrVoidReturningVoid), cbFunc(cb)
{}
CallbackRecord(int (*cb)()): cbType(eFuncPtrVoidReturningInt),
cbFunc(reinterpret_cast<void (*)()>(cb)) {}
void operator()() const;
protected:
CallbackType cbType;
void (*cbFunc)();
};
void CallbackRecord::operator()() const
{
switch(cbType)
{
case eFuncPtrVoidReturningVoid:
(*cbFunc)();
break;
case eFuncPtrVoidReturningInt:
while((*reinterpret_cast<int (*)()>(cbFunc))())
;
break;
}
}
虽然你可以只说“让所有的回调返回int
”,这将要求你写的东西包装不符合调用约定,如果回调类型的数字变超过两个。允许这些函数指针类型转换为您提供了一种支持多种回调类型的替代方法,并使我们不需要将CallbackRecord
转换为模板。它还允许继承或编组来替代上面的switch
声明,而不需要使用virtual
方法。
是的,它是安全的。 reinterpret_cast
只允许在一个指针转换为另一种,但它并不能保证,除非它被强制转换-ED恢复到原来的任何类型的安全性和使用这种指针的结果是不确定的。
提到的标准报价规定,如果强制转换一个类型的函数指针为另一种类型,并试图通过它来调用一个函数,那么结果是不确定的。
但是,reinterpret_cast
保证您如果将类型转换指针转换回原始类型,则指针结构良好。
你的代码试图做第二次,因此它是安全的。
我想其他的转换就像'f'到'fp1'在我的情况下,对吗? – user1086635
@user:是的,没有指定'fp1'的内容,但是如果您转换回原始类型,则会返回原始值。 – sth
未定义的行为是,如果你试图做'(* fp1)();'。这会调用一个返回'int'的函数,就像它返回'void'一样。至少这取决于你的架构的ABI。如果它使用旧的C方法,在调用函数之前为堆栈中返回的对象分配空间,那么在这种情况下,它不会为返回的“int”分配空间(因为它认为没有返回值),并且被调用的函数会将其返回值放在错误的地方(例如覆盖返回地址),并且事情从此处下坡。 –