试图了解模板和名称查找


9

我正在尝试了解以下代码片段

片段1

template <typename T>
struct A
{
    static constexpr int VB = T::VD;
};

struct B : A<B>
{
};

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;
};

在这种情况下,gcc9可以正常编译,但是clang9会引发错误,提示“ B中没有名为AD的成员”。

问:为什么用gcc9编译/为什么不用clang9编译?

片段3

template <typename T>
struct A
{
    using TB = typename T::TD;
};

struct B : A<B>
{
    using TD = int;
};

在这里,clang9和gcc9都会引发错误。gcc9说“无效使用了不完整类型'struct B'”。

问:如果这里的结构B不完整,那么为什么在代码段2中它不完整?

使用的编译器标志:-std=c++17 -O3 -Wall -Werror。提前致谢!!!


@xception是不是struct B实例AB
可变副作用

clang9引发错误,提示“ B中没有名为AD的成员”。由于B是不完整的......但无法确定何时成员应该被实例化..
Jarod42

@MutableSideEffect哦,是的,我不好,也
应将

@ Jarod42那么为什么gcc可以正常编译?
可变副作用

1
我已将此问题标记为“需要更多关注”,并且该问题确实包含多个问题(因此得出我的结论),那么为什么我的标记错误?
多米尼克

Answers:


4

我相信这些本质上可以归结为[temp.inst] / 2(强调我的观点):

类模板专业化的隐式实例引起声明的隐式实例,但没有定义,默认参数,或noexcept-符 类成员函数,成员类,作用域成员枚举,静态数据成员,成员模板,和朋友 […]

[temp.inst] / 9

除非需要这样的实例化,否则实现不应隐式实例化[…]类模板[…]的静态数据成员。

标准中有关隐式模板实例化的措辞使许多细节有待解释。通常,在我看来,除非规范明确说明,否则您根本无法依赖实例化的模板部分。从而:

片段1

问:为什么要编译此代码?从B继承时,我们不是实例化A吗?B中没有VD,因此编译器不应该在这里抛出错误吗?

您正在实例化A<B>。但是实例化A<B>仅实例化声明,而不是其静态数据成员的定义。VB绝不会以需要定义存在的方式使用。编译器应接受此代码。

片段2

问:为什么用gcc9编译/为什么不用clang9编译?

正如Jarod42指出的那样,的声明AB包含一个占位符类型。在我看来,该标准的措词在这里应该发生的事情上并不清楚。包含占位符类型的静态数据成员的声明的实例化是否触发占位符类型推导,从而构成需要定义静态数据成员的使用?我找不到标准中明确表示是或否的措辞。因此,我想说这两种解释在这里都同样有效,因此,GCC和clang都是正确的……

片段3

问:如果这里的结构B不完整,那么为什么在代码段2中它不完整?

其中到达封闭的类型是只在该点处完成}所述的类说明符 [class.mem] / 6。因此,在所有代码段B的隐式实例化期间,都是不完整的A<B>。只是这与代码段1无关。在代码片段2中,铛确实给您一个错误No member named AD in B。与代码片段2的情况类似,我找不到确切实例化成员别名声明的措辞。但是,与静态数据成员的定义不同,没有措辞可以明确地防止在类模板的隐式实例化过程中成员别名声明的实例化。因此,我想说,在这种情况下,GCC和clang的行为都是对该标准的有效解释。


谢谢。在这种情况下,我们将初始化体内的静态数据成员。初始化是声明的一部分,还是静态数据成员的定义的一部分?我的印象是,如果初始化在主体内,则这是静态数据成员的声明的一部分。如果它是声明的一部分,则您的第一个引号需要立即实例化,作为类模板周围隐式实例化的一部分。
Johannes Schaub-litb

我查看了该规范,似乎这里的C ++ 14和C ++ 17之间存在差异。在C ++ 14中,constexpr静态数据成员只是一个声明。C ++ 17获得了inline变量并constexpr隐含了inline,这使体内静态数据成员声明成为定义。
Johannes Schaub-litb

eel.is/c++draft/dcl.spec.auto#4.sentence-2说:“使用占位符类型声明的变量的类型是从其初始值设定项推导的。在初始化声明中可以使用这种用法([dcl。 init])。”。因此,我认为必须定义静态数据成员,因为它包含初始化程序。
Johannes Schaub-litb

@ JohannesSchaub-litb感谢您的关注!我也一直在想这些问题,但找不到任何结论性的措辞。关于初始化与定义之间的关系,请考虑在类模板的定义内定义成员函数。这样的定义也是一个声明,没有其他声明。但是,类模板的隐式实例化只会实例化声明,而不会实例化成员函数的定义。为什么对于静态数据成员来说这是不正确的呢?
Michael Kenzel

如果没有什么需要定义的,则不会实例化定义。但是在这种auto情况下,规则说声明必须是初始化声明。只有在知道声明是一个定义的情况下,这种情况才会发生(据我所知。我离开律师界已有一段时间了)。过去有一个类似的案例,并添加了eel.is/c++draft/temp.inst#2.sentence-3,其中将定义的声明实例化为“已知是定义”,而实际上没有实例化定义。
Johannes Schaub-litb
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.