模板类和相同翻译单元中的相应extern模板类

时间:2012-12-24 12:12:26

标签: c++ templates c++11 extern

这是C ++ 11中extern模板的正确用法吗? (可以是extern template class和相应的template class在同一个翻译单元中可见吗?)

// example.hpp:
#pragma once
template< typename T >
class C {
    void f(T);
};
// question is about the next two lines
extern template class C< float >;
extern template class C< double >;
// example_def.hpp:
#include "example.hpp"
template< typename T >
void C< T >::f(T) {
    //... smth. practicable
}
// example.cpp:
#include "example_def.hpp"
template class C< float >;
template class C< double >;
// other.hpp:
#pragma once
void g();
// other.cpp:
#include "other.hpp"
#include "example.hpp"
// maybe those two lines should be here instead?
void g() {
    C< float >();
    C< double >();
}
// main.cpp:
#include "example.hpp"
#include "other.hpp"
// ...and here?
int main() {
    C< float >();
    C< double >();
    g();
    return 0;
}

2 个答案:

答案 0 :(得分:4)

是的,extern template class规范(标准称为显式实例化声明 )和template class规范(称为 explicit)如果定义(没有extern)遵循声明(使用extern),则标准的实例化定义 可以在同一个翻译单元中:

  

(§14.7.2/ 11)如果实体是同一翻译单元中的显式实例化声明和显式实例化定义的主题,则定义应遵循声明。实体这是一个显式实例化声明的主题,并且也会以一种方式使用,否则会导致翻译单元中的隐式实例化(14.7.1),它应该是程序中某个地方的显式实例化定义的主题;否则该程序格式错误,无需诊断。 [注意:此规则确实适用于内联函数,即使此类实体的显式实例化声明没有其他规范性效果。这需要确保如果内联函数的地址是在实现选择抑制外线主体的翻译单元中进行的,则另一个翻译单元将提供主体。 - 结束注释]显式实例化声明不应命名具有内部链接的模板的特化。

(强调我的)。术语显式实例化声明显式实例化定义在此处定义:

  

(§14.7.2/ 2)显式实例化的语法是:

     

显式实例:
        extern opt template 声明

     

有两种形式的显式实例化:显式实例化定义和显式实例化声明。显式实例化声明以extern关键字开头。


这些显式实例化的效果如下:

  1. 显式实例化声明(使用extern)可防止所有隐式实例化生效(内联函数和类模板特化除外,§除外) 14.7.2 / 10)。

  2. 显式实例化定义(没有extern)导致实例化发生无论什么,即它覆盖显式实例化声明(这也遵循§14.7.2) / 10)。


  3. 一般性评论
    您的显式实例化声明位于定义模板的头文件中这一事实意味着包含头文件的任何人为了使用该模板,还必须添加一个显式实例化< em> definition ,或者,需要链接到包含这种显式实例化定义的另一个.cpp文件的代码。

    当您期望许多不同的用户为许多不同类型实例化模板时,这可能会令人困惑,并且可能不是一个好主意。但是,如果不同类型的实例化数量很少并且您可以预测它们,那么这是明智的。当然,您必须确保有一个(或几个).cpp个文件包含所需的所有实例化的显式实例化定义,并且其相应的目标文件与之链接建设时的项目。

答案 1 :(得分:3)

extern模板的基本思想是支持常用实例化的显式实例化,同时还支持不太常用的参数的隐式实例化。例如,std::basic_string<char>可以显式地实现,但std::basic_string<signed char>可以留给隐式实例化(实际的激励示例是IOStreams,需要花费大量时间来实例化,但实际上只使用了两个实例)。

为了允许隐式实例化,使用模板的定义需要在使用模板的每个翻译单元中可见。如果模板定义可见,则编译器默认情况下假定它需要隐式提供实例化。使用extern模板声明告诉编译器特定模板实例化将由某个翻译单元提供。

虽然您的案例有效,但甚至不需要声明extern模板:当编译器使用实例化但未找到其定义时,它将假定在某些翻译单元中找到实例化。 / p>