为什么内联用户提供的构造函数 odr-uses base class 构造函数?
Why does an inline user-provided constructor odr-uses base class constructors?
考虑以下示例
#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() {
}
当上述程序被GCC, Clang or VC++编译并执行时,可以看到如下输出:
Initialized!
所有三个编译器都发出 Base<A>::touch
的定义和初始化,而没有发出 Base<B>::touch
的定义和初始化(也通过 godbolt 验证)。所以我会得出结论,这是标准的制裁行为。
对于B
的默认构造函数,我们有
[class.ctor]
7 A default constructor that is defaulted and not defined as deleted is implicitly defined when it is odr-used to create an object of its class type ([intro.object]) or when it is explicitly defaulted after its first declaration.
从中可以得出结论,由于这两个条件都不适用于我们的 TU,因此从未隐式定义 B::B()
。所以它从不使用 Base<B>::Base()
和 Base<B>::touch
。我觉得这很合理。
但是,我无法理解为什么 A::A()
最终会使用其基础 class 的成员。我们知道
[class.mfct]
1 A member function may be defined in its class definition, in which case it is an inline member function ...
[dcl.inline]
6 An inline function or variable shall be defined in every translation unit in which it is odr-used and shall have exactly the same definition in every case ([basic.def.odr]).
[basic.def.odr]
4 ... A constructor for a class is odr-used as specified in [dcl.init].
我们从不初始化任何 A
类型的对象,所以我们不应该使用它的构造函数。所以我们的程序最终不会包含 A::A()
.
的任何定义
那么为什么它的行为就像存在定义一样?为什么它 odr-use Base<A>::Base()
并导致它的实例化?
这发生在 任何 内联定义中,odr 使用 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
B() = default
不 odr-use Base<T>::Base
因为它只被定义为 defaulted. Even though structurally a definition according to [basic.def]/2,因为 [class.ctor]/7 (正如你引用的那样)。只有当 B::B()
本身被 odr 使用时,才会(隐含地)定义 odr-use Base<T>::Base
的定义。
对于定义的其他内联函数,不存在这样的豁免。他们的定义无条件地(和结构上)包含一个 odr-use Base<T>::Base
。 A::A()
在您的示例中是一个定义,您的程序 确实 包含使用 Base<T>::Base
.
的 A::A()
的定义
“应在使用 odr 的每个翻译单元中定义内联函数或变量”。这是一个“应”要求(对于每个使用内联函数的 TU,都需要一个定义),而不是使用内联函数的 odr 的结果。
[1] 由于 [dcl.fct.def.default]/5:
A user-provided explicitly-defaulted function (i.e., explicitly defaulted after its first declaration) is defined at the point where it is explicitly defaulted
所以 C::C()
有一个默认定义,它会立即生成“隐式”定义。
考虑以下示例
#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() {
}
当上述程序被GCC, Clang or VC++编译并执行时,可以看到如下输出:
Initialized!
所有三个编译器都发出 Base<A>::touch
的定义和初始化,而没有发出 Base<B>::touch
的定义和初始化(也通过 godbolt 验证)。所以我会得出结论,这是标准的制裁行为。
对于B
的默认构造函数,我们有
[class.ctor]
7 A default constructor that is defaulted and not defined as deleted is implicitly defined when it is odr-used to create an object of its class type ([intro.object]) or when it is explicitly defaulted after its first declaration.
从中可以得出结论,由于这两个条件都不适用于我们的 TU,因此从未隐式定义 B::B()
。所以它从不使用 Base<B>::Base()
和 Base<B>::touch
。我觉得这很合理。
但是,我无法理解为什么 A::A()
最终会使用其基础 class 的成员。我们知道
[class.mfct]
1 A member function may be defined in its class definition, in which case it is an inline member function ...
[dcl.inline]
6 An inline function or variable shall be defined in every translation unit in which it is odr-used and shall have exactly the same definition in every case ([basic.def.odr]).
[basic.def.odr]
4 ... A constructor for a class is odr-used as specified in [dcl.init].
我们从不初始化任何 A
类型的对象,所以我们不应该使用它的构造函数。所以我们的程序最终不会包含 A::A()
.
那么为什么它的行为就像存在定义一样?为什么它 odr-use Base<A>::Base()
并导致它的实例化?
这发生在 任何 内联定义中,odr 使用 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
B() = default
不 odr-use Base<T>::Base
因为它只被定义为 defaulted. Even though structurally a definition according to [basic.def]/2,因为 [class.ctor]/7 (正如你引用的那样)。只有当 B::B()
本身被 odr 使用时,才会(隐含地)定义 odr-use Base<T>::Base
的定义。
对于定义的其他内联函数,不存在这样的豁免。他们的定义无条件地(和结构上)包含一个 odr-use Base<T>::Base
。 A::A()
在您的示例中是一个定义,您的程序 确实 包含使用 Base<T>::Base
.
A::A()
的定义
“应在使用 odr 的每个翻译单元中定义内联函数或变量”。这是一个“应”要求(对于每个使用内联函数的 TU,都需要一个定义),而不是使用内联函数的 odr 的结果。
[1] 由于 [dcl.fct.def.default]/5:
A user-provided explicitly-defaulted function (i.e., explicitly defaulted after its first declaration) is defined at the point where it is explicitly defaulted
所以 C::C()
有一个默认定义,它会立即生成“隐式”定义。