3

这是 C++11 中外部模板的正确用法吗?(可以在同一个翻译单元中看到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;
}
4

2 回答 2

4

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

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

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

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

显式实例化:
extern选择 template 声明

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


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

  1. 显式实例化声明(with extern)阻止所有隐式实例化生效(内联函数和类模板专业化,§14.7.2/10 除外)。

  2. 显式实例化定义(不带extern)导致实例化无论如何都会发生,即它覆盖显式实例化声明(这也遵循 §14.7.2/10)。


一般评论
您的显式实例化声明位于定义模板的头文件中这一事实意味着,为了使用模板而包含头文件的任何人都必须同时添加显式实例化定义,或者,需要链接到.cpp包含此类显式实例化定义的另一个文件的代码。

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

于 2012-12-24T12:23:40.427 回答
3

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

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

尽管您的案例有效,但甚至不需要声明extern模板:当编译器使用实例化并且找不到它的定义时,它将假定在某个翻译单元中找到了实例化。

于 2012-12-24T13:02:44.593 回答