使用`extern template`防止模板类的隐式实例化

时间:2019-06-04 17:10:59

标签: c++ c++11 templates language-lawyer extern

请考虑以下代码段:

template <typename>
struct X { };

extern template struct X<int>;

int main()
{
    X<int>{};
}

它编译并链接:live example on godbolt.org。由于extern template声明,我希望它不会链接。

我的理解是extern template的意思是:“请不要在此TU中实例化此特定模板的特殊化,它将由其他TU提供,您可以对其进行链接”

示例/说明。我在 isocpp cppreference 上看到过,似乎可以验证我的心理模型。例如

  

来自https://en.cppreference.com/w/cpp/language/class_template

     
    

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

  

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


编辑-在最新的标准草案中找到了此内容

  

[temp.explicit]

     
    

如果实体是同一翻译单元中的显式实例化声明和显式实例化定义的主题,则该定义应位于声明之后。 作为显式实例化声明的主题的实体,并且还应以其他方式在翻译单元中导致隐式实例化的实体的使用,应作为程序中某个位置的显式实例化定义的主题;否则程序格式错误,无需诊断。

  

这是否意味着我发布的代码段是格式错误的NDR

2 个答案:

答案 0 :(得分:4)

  

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

好吧,没有链接。因为必须考虑显式实例化的影响。来自n3337:

  

[temp.explicit] (强调我的意思)

     

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使用了永远不会实例化其定义的构造函数。声明 被实例化,因为如上所述,封闭的专业化被实例化。但是,在显式实例化声明的抑制作用下,我们从未得到任何定义。

答案 1 :(得分:4)

  

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

是的,根据您引用的[temp.explicit] / 13中的确切句子。 “实体”就是这样。显式实例化声明是否没有其他规范作用都没有关系。