【问题标题】:Cast function pointers that differs by argument type转换因参数类型而异的函数指针
【发布时间】:2026-01-31 01:00:01
【问题描述】:

为什么这是不合法的:

class Base
{
public:
    Base(){};
    virtual ~Base(){};
};

class Derived : public Base{};

void takeDerived(Derived * c){};

// main
void(*ptr)(Base*) = static_cast<void(*)(Base*)>(&takeDerived); // doesn't work

// but this work ok, as well as reinterpret_cast
// void(*ptr)(Base*) = (void(*)(Base*))(&takeDerived);

Derived 是一个Base。为什么不能在函数参数中强制转换?例如,即使没有强制转换,我也可以轻松做到这一点:

void takeBase(Base* c){};
takeBase(new Derived{});

【问题讨论】:

  • 那将违反基本的理智。 takeDerived 期望 Derived *,但您的 ptr 将接受 any Base-派生指针!

标签: c++ casting function-pointers


【解决方案1】:

它就是这样设计的。 Base 不是 Derived。类派生的 is-a 关系不能颠倒。

函数参数的类型表示“接受”,而对象的类型表示“适合”。转换函数的类型会改变它接受的内容。允许一个函数接受它最初不接受的东西是很危险的。

考虑这段代码:

class Base {};
class Derived : public Base {
    public:
    int t;
    void useDerived() {}
};

void useDerived(Derived *d){
    d->useDerived();
}

如果传递了Base 对象会发生什么?

Base b;
((void(*)(Base*))useDerived) (&b);

更糟糕的是,如果 Base 的另一个派生被传递了怎么办?

class AnotherDerived : public Base {
    public:
    double t;
    void useDerived() {}
};
AnotherDerived ad;
((void(*)(Base*))useDerived) (&ad);

【讨论】:

    【解决方案2】:

    是的,但您正试图以相反的方式进行操作。你做不到

    void takeDerived(Derived *c) { }
    
    ...
    
    Base b;
    takeDerived(&b);
    

    您尝试执行的函数指针转换会启用这些恶作剧;你可以这样做

    void (*ptr)(Base*) = takeDerived;
    Base b;
    ptr(&b); // Oops.
    

    然后事情会爆炸,那会很糟糕。

    【讨论】: