Mut*_*ect 8 c++ templates language-lawyer
我试图理解以下代码片段
片段 #1
template <typename T>
struct A
{
static constexpr int VB = T::VD;
};
struct B : A<B>
{
};
Run Code Online (Sandbox Code Playgroud)
gcc9 和 clang9 都不会在这里抛出错误。
问:为什么这段代码会编译?A<B>
从 B 继承时我们不是实例化了吗?B中没有VD,所以编译器不应该在这里抛出错误吗?
片段 #2
template <typename T>
struct A
{
static constexpr auto AB = T::AD; // <- No member named AD in B
};
struct B : A<B>
{
static constexpr auto AD = 0xD;
};
Run Code Online (Sandbox Code Playgroud)
在这种情况下,gcc9 编译得很好,但 clang9 抛出一个错误,说“B 中没有名为 AD 的成员”。
Q. 为什么用gcc9编译/为什么不用clang9编译?
片段 #3
template <typename T>
struct A
{
using TB = typename T::TD;
};
struct B : A<B>
{
using TD = int;
};
Run Code Online (Sandbox Code Playgroud)
这里 clang9 和 gcc9 都抛出错误。gcc9 说“不完整类型‘struct B’的无效使用”。
问:如果这里的结构 B 不完整,那么为什么它在代码段 #2 中不完整?
使用的编译器标志:-std=c++17 -O3 -Wall -Werror
. 提前致谢!!!
我相信这些本质上可以归结为[temp.inst]/2(强调我的):
\n\n\n\n\n\n\n类模板特化的隐式实例化会导致声明的隐式实例化,但不会导致类成员函数、成员类、作用域 成员枚举、静态数据成员、成员模板和朋友们; [\xe2\x80\xa6]
\n
\n\n\n除非需要这样的实例化,否则实现不得隐式实例化 [\xe2\x80\xa6] 类模板 [\xe2\x80\xa6] 的静态数据成员。
\n
标准中有关隐式模板实例化的措辞留下了许多可供解释的细节。一般来说,在我看来,你根本不能依赖模板的某些部分而不是规范明确说明,否则因此:
\n\n\n\n\n问:为什么这段代码可以编译?我们继承B的时候不是实例化了A吗?B 中没有 VD,那么编译器不应该在这里抛出错误吗?
\n
您正在实例化A<B>
. 但实例化A<B>
仅实例化声明,而不实例化其静态数据成员的定义。VB
永远不会以需要存在定义的方式使用。编译器应该接受此代码。
\n\n\n问:为什么它用 gcc9 编译/为什么不用 clang9 编译?
\n
正如 Jarod42 所指出的,AB
包含占位符类型。在我看来,标准的措辞对于这里应该发生的事情并不太清楚。包含占位符类型的静态数据成员的声明的实例化是否会触发占位符类型推导,从而构成需要定义静态数据成员的使用?我在标准中找不到明确表示是或否的措辞。因此,我想说这两种解释在这里同样有效,因此,GCC 和 clang 都是正确的\xe2\x80\xa6
\n\n\n问:如果这里的 struct B 不完整,那么为什么它在片段 #2 中不是不完整?
\n
}
类类型仅在到达类说明符 [class.mem]/6结束时才完整。因此,在所有片段B
的隐式实例化期间,它是不完整的。A<B>
只是这与代码片段 #1 无关。在代码片段 #2 中,clangNo member named AD in B
确实给了你一个错误。与代码片段 #2 的情况类似,我找不到有关何时实例化成员别名声明的确切措辞。但是,与静态数据成员的定义不同,没有适当的措辞来显式阻止在类模板的隐式实例化期间实例化成员别名声明。因此,我想说,在这种情况下,GCC 和 clang 的行为都是对标准的有效解释\xe2\x80\xa6