首页 文章

将指向函数的指针转换为指向函数类型的不同指针的结果

提问于
浏览
2

(5.2.10 / 6)C 03指向函数的指针可以显式转换为指向不同类型函数的指针 . 通过指向函数类型(8.3.5)的函数调用函数的效果是未定义的,该函数类型与函数定义中使用的类型不同 . 除了将“指向T1的指针”的rvalue转换为“指向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();
}

3 回答

  • 4

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

    除[特殊情况]外,未指定此类指针转换的结果 .

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

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

  • 5

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

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

    然而,
    reinterpret_cast 保证如果将类型转换指针强制转换回原始类型,那么指针就会很好地形成 .

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

  • 5

    是的,你很安全 . 你正在做的事情被“转换......除外”案件所涵盖 .

    它被调出的原因是让你通过另一种类型的函数指针传递函数指针 . 所以你可以定义类似的东西:

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

相关问题