考虑下面的代码片段:
template <typename>
struct X { };
extern template struct X<int>;
int main()
{
X<int>{};
}
它编译并链接:。由于extern template
声明,我希望它不会链接。
我的理解是,extern template
的意思是:“请不要在这个TU中实例化这个特定的模板专门化,它将由其他TU提供,您可以链接到它”。
示例/描述。我在isocpp和cppreference上看到的似乎验证了我的心理模型。例如。
来自https://en.cppreference.com/w/cpp/language/class_template的
:
显式实例化声明(外部模板)跳过隐式实例化步骤:将导致隐式实例化的代码改为使用在别处提供的显式实例化定义(如果不存在此类实例化,则会导致链接错误)。通过在使用模板实例化的所有源文件中显式声明模板实例化,并在其余文件中显式定义模板实例化,可以使用这种方法来减少编译时间。
为什么我的代码片段链接?这里到底发生了什么?
编辑-在最新的标准草案中找到以下内容:
如果一个实体在同一翻译单元中同时是显式实例化声明和显式实例化定义的主体,则定义应跟随在声明之后。如果实体是显式实例化声明的主题,并且其使用方式也会导致转换单元中的隐式实例化,则该实体应成为程序中某处显式实例化定义的主题;否则,该程序的格式不正确,不需要诊断。
这是否意味着我发布的代码片段是格式错误的,NDR
发布于 2019-06-05 01:46:00
为什么我的代码片段链接?这里到底发生了什么?
好吧,这没什么可联系的。因为必须考虑显式实例化的影响。来自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使用的构造函数的定义从未实例化。声明是实例化的,因为如上所述,封闭的专门化是实例化的。但是在显式实例化声明的抑制作用下,我们永远得不到任何定义。
发布于 2019-06-05 03:29:01
这是否意味着我发布的代码片段是格式错误的NDR?
是的,就像你引用的temp.explink/13中的句子一样。“一个实体”就是这个意思。如果显式实例化声明在其他方面没有规范效果,这并不重要。
https://stackoverflow.com/questions/56448493
复制相似问题