typedef 别名的析构函数

Destructor of typedef alias

#include <iostream>

struct A { ~A(); };
A::~A() {
    std::cout << "Destructor was called!" << std::endl;
}

typedef A AB;
int main() {
    AB x;
    x.AB::~AB(); // Why does this work?
    x.AB::~A();
}

以上程序的输出为:

Destructor was called!
Destructor was called!
Destructor was called!

我假设前两行属于用户析构函数调用,而第三行是由于退出 main 函数范围时调用的析构函数。

根据我的理解,typedef 是类型的别名。在这种情况下 ABA.

的别名

为什么这也适用于析构函数的名称?非常感谢对语言规范的引用。

编辑:这是在 macOS High Sierra 版本 10.13.3 上使用 Apple LLVM 版本 9.1.0 (clang-902.0.39.1) 编译的。

Why does this apply for the name of the destructor too?

因为标准说:

[class.dtor]

In an explicit destructor call, the destructor is specified by a ~ followed by a type-name or decltype-specifier that denotes the destructor’s class type. ...

typedef 别名是一个类型名称,它表示与 class 本身的类型名称相同的 class。

这条规则甚至还有一个说明性的例子:

struct B {
  virtual ~B() { }
};
struct D : B {
  ~D() { }
};

D D_object;
typedef B B_alias;
B* B_ptr = &D_object;

void f() {
  D_object.B::~B();             // calls B's destructor
  B_ptr->~B();                  // calls D's destructor
  B_ptr->~B_alias();            // calls D's destructor
  B_ptr->B_alias::~B();         // calls B's destructor
  B_ptr->B_alias::~B_alias();   // calls B's destructor
}

关于名称查找的进一步规范,以及适用于问题的示例:

[basic.lookup.qual]

If a pseudo-destructor-name ([expr.pseudo]) contains a nested-name-specifier, the type-names are looked up as types in the scope designated by the nested-name-specifier. Similarly, in a qualified-id of the form:

nested-name-specifieropt class-name :: ~ class-name

the second class-name is looked up in the same scope as the first. [ Example:

struct C {
  typedef int I;
};
typedef int I1, I2;
extern int* p;
extern int* q;
p->C::I::~I();      // I is looked up in the scope of C
q->I1::~I2();       // I2 is looked up in the scope of the postfix-expression

struct A {
  ~A();
};
typedef A AB;
int main() {
  AB* p;
  p->AB::~AB();     // explicitly calls the destructor for A
}

— end example  ]

因为当您编写 ~AB() 时,您并没有命名或调用析构函数。您正在编写 ~ 后跟 the class 的名称,并且由于接下来编写这些标记的指定语义,会自动提供析构函数调用彼此。

通常这是学术性的,但在这里你会明白为什么它很重要。

类似地,通过编写 AB() 你不是 "calling a constructor",即使这看起来像一个函数调用,而且许多语言新手都是这样解释代码的。 (当试图在不推导参数的情况下调用模板构造函数时,这可能会带来乐趣和游戏:无法命名构造函数,就无法提供这些参数!)

事实上,从技术上讲,构造函数和析构函数都没有名字!

这些细微差别让 C++ 变得有趣,对吧?