在 cppreference.com – “限定名称查找”,我发现了这个奇怪的代码示例:
struct C { typedef int I; };
typedef int I1, I2;
extern int *p, *q;
// Slight modification to prevent linker error
//struct A { ~A(); };
struct A { ~A() {}; };
typedef A AB;
int main()
{
p->C::I::~I(); // The name I after ~ is looked up in the same scope as I before ::
// (that is, within the scope of C, so it finds C::I)
q->I1::~I2(); // The name I2 is looked up in the same scope as I1
// (that is, from the current scope, so it finds ::I2)
AB x;
x.AB::~AB(); // The name AB after ~ is looked up in the same scope as AB before ::
// (that is, from the current scope, so it finds ::AB)
}
令我惊讶的是,编译没有任何错误。但到底发生了什么
p->C::I::~I();
q->I1::~I2();
?这不仅看起来像是访问
int
变量的成员并以某种方式引用 int
析构函数,而且 p
和 q
也是没有任何定义的 extern
变量。
为什么允许这种语法,它实际上做了什么?
这些是伪析构函数调用。不使用类型别名的限定查找的直接表示法是
p->~int();
q->~int();
如果
x.~T()
是标量类型而不是类类型,则符号 p->~T()
或 T
始终是伪析构函数调用。在这种情况下,x
必须具有类型T
和p
类型int*
。
伪析构函数调用结束对象的生命周期
x
/*p
,就像普通的析构函数调用一样,但不执行任何其他操作。它的存在只是为了让人们可以编写适用于类和非类类型的通用代码,而无需在使用析构函数调用时编写不同的情况。
但是 p 和 q 也是没有任何定义的外部变量。
伪析构函数调用 odr-use
p
和 q
,因此程序中必须存在它们的定义。但是,它可能位于不同的翻译单元中。即使它不存在,这样的 odr 违规也会使程序 IFNDR(格式错误,无需诊断),因此编译器不会have 抱怨它。毕竟,这些行实际上不会导致发出任何机器代码,因此没有理由打扰链接器寻找 p
和 q
的定义。