【问题标题】:C++11: Explicit instantiation declaration vs. explicit instantiation definitionC++11:显式实例化声明与显式实例化定义
【发布时间】:2014-09-23 06:34:58
【问题描述】:

C++03的显式模板实例化definition和C++11的显式模板实例化declaration有什么区别?

我的意思是为什么实例化定义不足以阻止编译器为其他类型生成实现?下面的例子有什么问题:假设我将模板声明和定义分成两个单独的文件:

啊.h

#pragma once

template<typename T>
class A
{
public:
    A(T t);

private:
    T _t;
};

A.cpp

#include "A.h"

template<typename T>
A<T>::A(T t) : _t(t) {}

template class A<int>; // explicit instantiation

main.cpp

#include "A.h"

int main()
{
    A<int> a(5); // fine, compiler generates header file,
                 // linker links with implementation from A.cpp file

    // A<float> b(3.14f); // linker error, as expected
}

上面的例子中是否有任何编译时间开销?如果我理解正确,在这种情况下,在单独的 *.cpp 文件(以及模板的实现)中使用显式实例化 definition 我会使编译器无法使用任何其他类型隐式实例化模板。因此,为什么显式实例化声明有单独的语法?

如果我已经使用显式实例化定义在 A.cpp 文件中隐藏了实现,并阻止编译器生成正文,那么显式实例化 declaration 如何加快编译时间对于其他类型。 “显式实例化声明”是否与“显式实例化定义”有某种关联,我的意思是我应该同时使用它们,还是这些是完全独立的功能(例如,只有在未使用显式实例化定义时才能使用显式实例化声明)?

我想对了吗,显式实例化定义只是在没有其他翻译单元使用给定类型实例化模板的情况下触发错误?

【问题讨论】:

标签: c++ templates c++11


【解决方案1】:

当您在文件A.cpp 中放置显式实例化定义时,编译器在编译main.cpp 时应该如何知道它的存在?答案是它不能,所以它仍然会实例化main.cpp 中使用的模板,在您的情况下,使用显式实例化定义是无用的,也无济于事。

显式实例化的声明告诉编译器“不要费心实例化这个模板,我自己会在程序的其他地方做”,而定义就是实现这个承诺的东西。

显式实例化必须在一个文件中只定义一次,但可以在不同文件中多次声明。这不是模板独有的,在 C 和 C++ 中,相同的规则适用于(非内联)函数:您可以在不同的文件中多次声明它们(通常通过将声明放在标题中),然后您必须准确定义函数一次,在一个文件中。

因此,为了使您的示例正常工作,您应该在 A.h 中添加声明:

extern template class A<int>; // explicit instantiation declaration

【讨论】:

  • 如果我添加另一个翻译单元,比如 B.cpp,我只需要像在 main.cpp 中一样包含“A.h”,即可使用 A。这有什么问题?如果示例有效,我仍然不清楚为什么我需要显式模板声明。
  • @MarcAndreson,它可以工作,但编译器将在每个文件中再次实例化模板,链接器将丢弃重复项。那是浪费时间。在A.cpp 中使用显式实例化意味着编译器只需要生成一次类,但要利用这一点,编译器在编译B.cppmain.cpp必须知道存在显式实例化 ,这就是你声明它的原因。但是,如果您不了解其好处,那就不要使用显式实例化,没有人会强迫您使用它们。
  • 编译器在B.cpp 中实例化类A&lt;int&gt; 并发出对函数A&lt;int&gt;::A(int) 的未定义引用。然后链接器在链接时找到定义。如果在A.h 中使用显式实例化定义,那么编译器在编译B.cpp 时不会实例化A&lt;int&gt;
  • 所以编译器只是生成类的声明 A 对吧?就好像我在我的翻译单元中包含了“A_int”类的头文件一样?但我猜“extern template class A”会做同样的事情,所以我看不出有什么好处,但我希望我能理解它:(
【解决方案2】:

显式实例化声明(extern 模板)跳过隐式实例化步骤:否则会导致隐式实例化的代码改为使用其他地方提供的显式实例化定义(如果不存在此类实例化,则会导致链接错误)。这可用于通过在除一个使用它的源文件之外的所有源文件中显式声明模板实例化并在剩余文件中显式定义它来减少编译时间。 Source

这里main.cppA.h 中的声明将阻止main.cpp 根据模板的可见部分生成(在编译时)代码,即基于A.hA&lt;int&gt; 特化:

class A<int>
{
public:
    A(int t);
private:
    int _t;
};

但是main.cpp 代码确实需要知道A&lt;int&gt;::A(int) 的存在,这意味着它必须执行某种实例化才能了解所有A&lt;int&gt; 成员,这正是基于A.hA&lt;int&gt;实例化就可以了。

所以是的,它在这里的用处不是很明显——尽管取决于每个编译器/平台的分析。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-05
    相关资源
    最近更新 更多