3

(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();
}
4

3 回答 3

5

是的,它是安全的。
reinterpret_cast只允许您将一个指针转换为另一个指针,但它不保证任何安全性,并且使用这种指针的结果是未指定的,除非它被类型转换回其原始类型。

提到的标准引用指定,如果您将一种类型的函数指针类型转换为另一种类型并尝试通过它调用函数,则结果是未定义的。

但是,
reinterpret_cast向您保证,如果您将类型转换的指针转换回原始类型,那么指针的格式是正确的。

您的代码尝试执行第二个,因此它是安全的。

于 2011-12-11T15:10:57.983 回答
5

您误读了标准,“未指定”部分仅适用于其他转换:

除了[特殊情况],这种指针转换的结果是未指定的。

[特殊情况] 是您转换回原始函数指针类型的情况,就像在您的示例中一样。在这种特殊情况下,转换会产生原始指针值,因此可以像在您的示例中一样使用它。

仅对于其他转换,结果未指定。

于 2011-12-11T15:18:41.743 回答
4

是的,你很安全。“除了转换......”案例涵盖了您正在做的事情。

调用它的原因是让您通过另一种类型的函数指针传递函数指针。因此,您可以定义如下内容:

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方法。

于 2011-12-11T15:10:32.747 回答