【问题标题】:C++11 extern templates: where do we actually need them?C++11 外部模板:我们在哪里真正需要它们?
【发布时间】:2017-03-09 22:23:27
【问题描述】:

在 C++03 中,我们有模板显式实例化定义 (template class Foo<int>),它强制实例化模板类。

在 C++11 中,我们有 模板显式实例化声明 (extern template class Foo<int>),它可以防止模板类的隐式实例化。 (Class template instantiation)

我正在尝试模拟实际需要显式实例化声明以减少编译时间的情况。但我不能。没有此功能(或无法使用)似乎一切正常。

这是一个例子:

//Foo.h
#pragma once
template<class T>
class Foo
{
    T inst;
public:
    Foo(T i);
    T& get() const;
};


//Foo.cpp
#include "stdafx.h"
#include "Foo.h"

template<class T>
Foo<T>::Foo(T inst) : inst(inst) { }

template<class T>
T& Foo<T>::get() const { return inst; }

template class Foo<int>; //explicit instantiation definition


//test1.h
#pragma once
#include "Foo.h"

//This line does not work
//extern template class Foo<int>; //explicit instantiation declaration.

void baz();


//test1.cpp
#include "stdafx.h"
#include "test1.h"

void baz()
{
    Foo<int> foo(10);
    int i = foo.get();
}

结果取决于我是否评论 (extern template class Foo&lt;int&gt;;) 行。

这是两个 *.obj 文件的符号:

dumpbin /SYMBOLS test1.obj

011 00000000 UNDEF notype () 外部 | ??0?$Foo@H@@QAE@H@Z (public: __thiscall Foo::Foo(int))'

012 00000000 UNDEF notype () 外部 | ?get@?$Foo@H@@QBEHXZ (public: int __thiscall Foo::get(void)const )

013 00000000 SECT4 notype () 外部 | ?baz@@YAXXZ (void __cdecl baz(void))

...

dumpbin /SYMBOLS Foo.obj

017 00000000 SECT4 notype () 外部 | ??0?$Foo@H@@QAE@H@Z (public: __thiscall Foo::Foo(int))

018 00000000 SECT6 notype () 外部 | ?get@?$Foo@H@@QBEHXZ (public: int __thiscall Foo::get(void)const )

注意 Foo&lt;int&gt;::Foo&lt;int&gt;(int)int Foo&lt;int&gt;::get(void)const 在 test1.obj 中标记为 UNDEF 的内容,这意味着它们必须在其他地方解析(即 Foo 仅编译一次)。

尝试 #2:

如果我在 Foo.h 文件中定义完整模板(没有显式实例化定义),那么 extern template 没有帮助 - 模板编译两次(在 test1.cpp 和 test2.cpp 中)。

示例:

//test1.h
#pragma once
#include "Foo.h"
void baz();


//test1.cpp
#include "stdafx.h"
#include "test1.h"
void baz()
{
    Foo<int> foo(10); //implicit instantiation of Foo<int>
    int i = foo.get();
}


//test2.h
#pragma once
#include "Foo.h"
extern template class Foo<int>;
void bar();


//test2.cpp
#include "stdafx.h"
#include "test2.h"
void bar()
{
    Foo<int> foo(10); //should refer to Foo<int> from test1.obj but IT IS NOT
    int i = foo.get();
}

这里是符号转储:

dumpbin /SYMBOLS test2.obj

01D 00000000 SECT4 notype () 外部 | ??0?$Foo@H@@QAE@H@Z (public: __thiscall Foo::Foo(int))

01E 00000000 SECT8 notype () 外部 | ?get@?$Foo@H@@QBEHXZ (public: int __thiscall Foo::get(void)const )

01F 00000000 SECT6 notype () 外部 | ?bar@@YAXXZ (void __cdecl bar(void))

dumpbin /SYMBOLS test1.obj

01D 00000000 SECT6 notype () 外部 | ?baz@@YAXXZ (void __cdecl baz(void))

01E 00000000 SECT4 notype () 外部 | ??0?$Foo@H@@QAE@H@Z (public: __thiscall Foo::Foo(int))

01F 00000000 SECT8 notype () 外部 | ?get@?$Foo@H@@QBEHXZ (public: int __thiscall Foo::get(void)const )

在 Foo 提供的两个 *.obj 文件中。

所以我的问题是显式实例化声明可能有什么用处?或者我在测试中遗漏了什么?

我使用VS2013编译器。

【问题讨论】:

    标签: c++11 templates extern explicit-instantiation


    【解决方案1】:

    这里很好地解释了为什么 ATTEMP#2 不能按我的意愿工作: Is there a bug with extern template in Visual C++?

    简而言之,当您在头文件中定义和实现模板时,编译器可能会内联它。然后它执行 显式实例化定义 不符合标准(14.7.2/10“显式实例化”)。

    所以我们需要强制编译器不要内联模板。例如,通过在声明之后实现它。

    template<class T>
    class Foo {
       ...
       T get() const;
    };
    
    template<class T>
    T Foo<T>::get() const
    { ... }
    

    【讨论】:

      猜你喜欢
      • 2011-06-30
      • 2011-06-10
      • 2014-06-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-23
      • 1970-01-01
      相关资源
      最近更新 更多