为什么禁止获取析构函数的地址?



c++标准在12.4.2中规定

[…不能取析构函数的地址。[…]

但是,可以在编译器没有任何抱怨的情况下获取类析构函数的包装器地址,如下所示:

struct Test {
    ~Test(){};
    void destructor(){
        this->~Test();
    }
};
void (Test::*d)() = &Test::destructor;

那么禁止直接取析构函数的地址的理由是什么呢?

构造函数和析构函数有些特殊。编译器经常调用它们时使用不同的约定(例如传递额外的hidden参数)。如果你把地址存起来,那编译器将丢失函数是构造函数的信息

其他答案讨论了调用约定的潜在差异,这是一个原因。

但是我也认为:获取析构函数的地址到底有多大用处还不清楚!你会用它做什么?调用它可能是非常不安全的——你会用它来进行相等比较吗?也许是为了确定物体的确切类型?还有其他的语言工具。

最终,如果析构函数是作为函数发出的,那么它确实有一个地址——但是语言考虑将该地址用于任何,甚至相等比较都是非常无用的,因此在语言层面上是被禁止的。

从技术上讲,编译器可以允许你这样做——也许强制它为一个void *指针或其他东西——但这样就必须指定给你的确切地址。编译器甚至也不总是将普通类作为函数发出析构函数——因此接受地址将要求它这样做。

它可能只对相等比较有用——定义相等保证是什么可能不值得,因为其他语言工具可能会实现同样的事情。

Tl;Dr:它只是被认为不安全且无用,所以语言禁止它。

显然不能保证析构函数存在。如果您尝试以下操作,并检查反汇编,您将注意到没有对析构函数的调用,也没有可引用的析构函数。

https://godbolt.org/z/6nYWf48Gz

struct Foo {
    Foo() {}
    int a = 3;
};
void fizz() {
    Foo foo;
}
int main() {
    Foo foo;
    foo.~Foo(); // call destructor if exists, otherwise noop
    fizz();
    return 0;
}

最新更新