考虑以下代码片段:
template <typename>
struct X { };
extern template struct X<int>;
int main()
{
X<int>{};
}
它编译并链接:Godbolt.org上的实时示例。由于extern template
声明,我希望它不会链接。
我的理解是extern template
:“请不要在此TU中实例化此特定模板的特殊化,它将由其他TU提供,您可以对其进行链接”。
示例/说明。我在isocpp和cppreference上看到过,似乎可以验证我的心理模型。例如
从https://en.cppreference.com/w/cpp/language/class_template:
显式实例化声明(外部模板)跳过隐式实例化步骤:否则会导致隐式实例化的代码将改用其他地方提供的显式实例化定义(如果不存在此类实例,则会导致链接错误)。通过在使用该源文件的一个源文件中显式声明一个模板实例化,并在其余文件中显式定义它,可以用于减少编译时间。
为什么我的代码段链接?这里到底发生了什么?
编辑-在最新的标准草案中找到了这一点:
如果一个实体是同一翻译单元中的显式实例化声明和显式实例化定义的主体,则该定义应遵循该声明。一个实体是显式实例化声明的主题,并且以某种方式使用,否则该实体将在翻译单元中引起隐式实例化,则该实体应是程序中某个位置的显式实例化定义的主题;否则,程序格式错误,无需诊断。
这是否意味着我发布的代码段格式不正确,即NDR?
为什么我的代码段链接?这里到底发生了什么?
好吧,没有什么可链接的。因为必须考虑显式实例化的影响。从n3337:
[temp.explicit](强调我的)
10 除内联函数和类模板专门化外,显式实例化声明还具有抑制其所引用实体的隐式实例化的作用。[注意:意图是作为显式实例化声明的主题的内联函数在odr-use([basic.def.odr])时仍将隐式实例化,以便可以将主体视为内联,但不能内联函数的脱机副本将在翻译单元中生成。—尾注]
因此X<int>
,不会抑制类模板专门化的隐式实例化。它也是一个聚合,因此它的初始化是内联的,因此我们无法进行链接。但是,如果有任何成员,则根据第8款将其删除:
命名类模板专门化的显式实例化也是其每个成员(不包括从基类继承的成员)的相同种类(声明或定义)的显式实例,该成员先前未明确地包含在包含显式实例化,除非如下所述。
因此,如果您没有类似的汇总,而是:
template <typename>
struct X {
X();
};
template <typename T>
X<T>::X() {}
extern template struct X<int>;
int main()
{
X<int>{};
}
正如您所料,这将失败,因为ODR使用了永远不会实例化其定义的构造函数。如上所述,声明被实例化,因为封闭的专业化被实例化。但是,在显式实例化声明的抑制作用下,我们从未得到任何定义。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句