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 是类型的别名。在这种情况下 AB
是 A
.
的别名
为什么这也适用于析构函数的名称?非常感谢对语言规范的引用。
编辑:这是在 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++ 变得有趣,对吧?
#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 是类型的别名。在这种情况下 AB
是 A
.
为什么这也适用于析构函数的名称?非常感谢对语言规范的引用。
编辑:这是在 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++ 变得有趣,对吧?