使用extern模板(C ++ 11)

问题描述 投票:96回答:4

图1:功能模板

TemplHeader.h

template<typename T>
void f();

TemplCpp.cpp

template<typename T>
void f(){
   //...
}    
//explicit instantation
template void f<T>();

Main.cpp的

#include "TemplHeader.h"
extern template void f<T>(); //is this correct?
int main() {
    f<char>();
    return 0;
}

这是使用extern template的正确方法,还是仅将此关键字用于类模板,如图2所示?

图2:类模板

TemplHeader.h

template<typename T>
class foo {
    T f();
};

TemplCpp.cpp

template<typename T>
void foo<T>::f() {
    //...
}
//explicit instantation
template class foo<int>;

Main.cpp的

#include "TemplHeader.h"
extern template class foo<int>();
int main() {
    foo<int> test;
    return 0;
}

我知道将所有这些放在一个头文件中是好的,但如果我们在多个文件中实例化具有相同参数的模板,那么我们会得到多个相同的定义,编译器会将它们全部删除(除了一个)以避免错误。我如何使用extern template?我们可以只将它用于类,还是可以将它用于函数?

此外,图1和图2可以扩展为模板位于单个头文件中的解决方案。在这种情况下,我们需要使用extern template关键字来避免多个相同的瞬间。这仅适用于课程或功能吗?

c++ templates c++11 extern
4个回答
158
投票

您应该只使用extern template强制编译器在您知道它将在其他地方实例化时不实例化模板。它用于减少编译时间和目标文件大小。

例如:

// header.h

template<typename T>
void ReallyBigFunction()
{
    // Body
}

// source1.cpp

#include "header.h"
void something1()
{
    ReallyBigFunction<int>();
}

// source2.cpp

#include "header.h"
void something2()
{
    ReallyBigFunction<int>();
}

这将导致以下目标文件:

source1.o
    void something1()
    void ReallyBigFunction<int>()    // Compiled first time

source2.o
    void something2()
    void ReallyBigFunction<int>()    // Compiled second time

如果两个文件都链接在一起,则会丢弃一个void ReallyBigFunction<int>(),从而导致浪费的编译时间和目标文件大小。

为了不浪费编译时间和目标文件大小,有一个extern关键字使编译器无法编译模板函数。当且仅当您知道它在其他地方使用相同的二进制文件时,您应该使用它。

source2.cpp更改为:

// source2.cpp

#include "header.h"
extern template void ReallyBigFunction<int>();
void something2()
{
    ReallyBigFunction<int>();
}

将导致以下目标文件:

source1.o
    void something1()
    void ReallyBigFunction<int>() // compiled just one time

source2.o
    void something2()
    // No ReallyBigFunction<int> here because of the extern

当这两者都链接在一起时,第二个目标文件将只使用第一个目标文件中的符号。不需要丢弃,也不需要浪费编译时间和目标文件大小。

这应该只在项目中使用,比如当你多次使用像vector<int>这样的模板时,你应该在除了一个源文件之外的所有文件中使用extern

这也适用于类和函数作为一个,甚至模板成员函数。


42
投票

维基百科有best description

在C ++ 03中,只要在转换单元中遇到完全指定的模板,编译器就必须实例化模板。如果模板在许多翻译单元中使用相同类型进行实例化,则可能会显着增加编译时间。在C ++ 03中无法阻止这种情况,因此C ++ 11引入了外部模板声明,类似于外部数据声明。

C ++ 03有这种语法来强制编译器实例化一个模板:

  template class std::vector<MyClass>;

C ++ 11现在提供了这种语法:

  extern template class std::vector<MyClass>;

它告诉编译器不要在此转换单元中实例化模板。

The warning: nonstandard extension used...

Microsoft VC ++曾经有过几年的non-standard version of this feature(在C ++ 03中)。编译器警告这一点,以防止需要在不同编译器上编译的代码的可移植性问题。

查看linked page中的示例,看它的工作方式大致相同。您可以预期消息将随着MSVC的未来版本而消失,当然除非同时使用其他非标准编译器扩展。


4
投票

模板的已知问题是代码膨胀,这是在调用类模板特化的每个模块中生成类定义的结果。为了防止这种情况,从C ++ 0x开始,可以在类模板特化之前使用关键字extern

#include <MyClass> extern template class CMyClass<int>;

模板类的显式实例化应仅在单个转换单元中进行,最好是具有模板定义的转换单元(MyClass.cpp)

template class CMyClass<int>;
template class CMyClass<float>;

0
投票

如果您之前使用过extern函数,则模板遵循完全相同的原则。如果没有,虽然简单的功能可能会有所帮助。此外,您可能希望将extern放在头文件中,并在需要时包含标题。

© www.soinside.com 2019 - 2024. All rights reserved.