使用"外部模板"来防止模板类的隐式实例化

Using `extern template` to prevent implicit instantiation of a template class

本文关键字:quot 实例化 外部 使用      更新时间:2023-10-16

请考虑以下代码片段:

template <typename>
struct X { };
extern template struct X<int>;
int main()
{
X<int>{};
}

它编译并链接:godbolt.org 上的实时示例。我希望它不会因为extern template声明而链接。

我的理解是,extern template的意思是:">请不要在此 TU 中实例化此特定模板专用化,它将由其他 TU 提供,您可以链接到它"。

示例/说明。我在isocppcpp偏好上看到似乎验证了我的心智模型。例如

从 https://en.cppreference.com/w/cpp/language/class_template:

显式实例化

声明(外部模板)跳过隐式实例化步骤:否则会导致隐式实例化的代码改用其他地方提供的显式实例化定义(如果不存在此类实例化,则会导致链接错误)。这可用于减少编译时间,方法是在除一个源文件之外的所有源文件中显式声明模板实例化,并在其余文件中显式定义它。

为什么我的代码片段会链接?这里到底发生了什么?


编辑 - 在最新的标准草案中找到了这一点:

[临时明确]

如果实体在同一翻译单元中同时是显式实例化声明和显式实例化定义的主体,则定义应遵循该声明。作为显式实例化声明的主题的实体,并且还以可能导致翻译单元中隐式实例化的方式使用,应是程序中某处显式实例化定义的主体;否则程序格式不正确,无需诊断。

这是否意味着我发布的代码段格式不正确,NDR

为什么我的代码片段会链接?这里到底发生了什么?

好吧,没有什么可以链接的。因为必须考虑显式实例化的影响。从 n3337:

[临时明确](强调我的)

10内联函数和类模板除外 专用化、显式实例化声明具有效果 抑制它们所指向的实体的隐式实例化 指。[ 注意:目的是内联函数是 显式实例化声明的主题仍将是 当使用 ODR 时隐式实例化 ([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 使用其定义的构造函数永远不会实例化。声明被实例化,因为封闭专用化实例化,如上所述。但是在显式实例化声明的抑制效果下,我们从未得到任何定义。

这是否意味着我发布的代码片段格式不正确,NDR?

是的,通过您引用的 [temp.explicit]/13 中的确切句子。"一个实体"就是这个意思。显式实例化声明是否没有规范效力并不重要。