Sto*_*ica 5 c++ templates one-definition-rule language-lawyer
考虑以下说明性示例
#include <iostream>
template <typename>
struct Base {
static int const touch;
Base() {
(void)touch;
}
};
template<typename CRTP>
int const Base<CRTP>::touch = []{
std::cout << "Initialized!\n";
return 0;
}();
struct A : Base<A> {
A() {}
};
struct B : Base<B> {
B() = default;
};
int main() {
}
Run Code Online (Sandbox Code Playgroud)
当上述程序由GCC、Clang或VC++编译并执行时,我们始终会看到以下输出:
#include <iostream>
template <typename>
struct Base {
static int const touch;
Base() {
(void)touch;
}
};
template<typename CRTP>
int const Base<CRTP>::touch = []{
std::cout << "Initialized!\n";
return 0;
}();
struct A : Base<A> {
A() {}
};
struct B : Base<B> {
B() = default;
};
int main() {
}
Run Code Online (Sandbox Code Playgroud)
所有三个编译器都会发出 的定义和初始化Base<A>::touch,而都不发出 的定义和初始化Base<B>::touch(也通过 godbolt 进行验证)。所以我得出的结论是这是标准的制裁行为。
对于 的默认构造函数B,我们有
[班级.ctor]
7当默认构造函数被默认且未定义为已删除时,当它被 odr 用于创建其类类型 ([intro.object]) 的对象时,或者当它在第一次声明后被显式默认时,它会被隐式定义。
由此可以得出结论,由于这两种条件都不适用于我们的 TU,B::B()因此从未隐式定义。所以它从不使用Base<B>::Base()和Base<B>::touch。我觉得这很合理。
但是,我不明白为什么A::A()最终会使用其基类的成员。我们知道
[类.mfct]
1成员函数可以在其类定义中定义,在这种情况下,它是内联成员函数...
[dcl.内联]
6内联函数或变量应在使用 odr 的每个翻译单元中定义,并且在每种情况下都应具有完全相同的定义 ([basic.def.odr])。
[基本.def.odr]
4 ... 类的构造函数按 [dcl.init] 中指定的方式使用。
我们从不初始化任何类型的对象A,因此我们不应该使用它的构造函数。因此我们的程序最终不会包含 的任何定义A::A()。
那么为什么它的表现就像存在定义一样呢?为什么它会被 ODR 使用Base<A>::Base()并导致其实例化?
对于odr-uses 的任何内联定义都会发生这种情况Base<T>::Base,例如:
inline void x() {
Base<char>();
}
struct A : Base<A> {
A(int) {}
void x() {
Base<int>();
}
};
struct C : Base<C> {
C();
};
inline C::C() = default; // See [1]
// Will print "Initialized!" four times
Run Code Online (Sandbox Code Playgroud)
B() = default不使用 odr,Base<T>::Base因为它仅定义为defaulted。尽管在结构上是根据[basic.def]/2 的定义,但由于 [class.ctor]/7 (如您引用的),它不会“发出”。Base<T>::Base仅当 odr 使用本身时,才会(隐式)定义B::B()odr 使用的定义。
对于定义的其他内联函数,不存在这样的豁免。它们的定义无条件(并且在结构上)包含 odr-use Base<T>::Base。A::A()在您的示例中是一个定义,并且您的程序确实A::A()包含使用的定义Base<T>::Base。
“内联函数或变量应在使用 odr 的每个翻译单元中定义”。这是一个“应”要求(对于每个使用 odr 函数的 TU,都需要一个定义),而不是 odr 使用内联函数的结果。
[1] 由于[dcl.fct.def.default]/5:
用户提供的显式默认函数(即,在第一次声明后显式默认)在显式默认的位置定义
因此C::C()有一个默认定义,它立即生成“隐式”定义。
| 归档时间: |
|
| 查看次数: |
125 次 |
| 最近记录: |