在没有'extern'的情况下分离模板类的定义/实例化

时间:2014-03-22 09:40:02

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

(不再那么新)C ++ 11标准为模板引入了extern关键字。它的目的是告诉编译器模板不应该在使用点实例化,而是它将在另一个翻译单元中实例化(因此在链接时会有一个实例化) - 至少AFAIK。

现在,即使在C ++之前的11时代,我们也使用类似的方法将模板类的声明/定义与其实例化分开,以加快编译速度,例如:像这样:

point.h :类定义

template <int dim> struct Point {
  ...
  void foo();
  ...
};

point.cpp :方法定义

#include "point.h"
template <int dim>
void Point<dim>::foo() {
  ...
}

point_2d.cpp :类实例化(2D版本)

#include "point.cpp"
template struct Point<2>;

point_3d.cpp :类实例化(3D版本)

#include "point.cpp"
template struct Point<3>;

main.cpp :使用2D和3D点

#include "point.h"
int main(int, char**) {
    Point<2> p;
    p.foo();
}

现在我想知道:

  • 我们的方法是有效的C ++(03或11)代码还是我们幸运的是它有效?
  • 使用C ++ 11,我们能够通过在point.cpp中加入main.cpp并声明extern template <int dim> struct Point;来实现同样的目标吗?

3 个答案:

答案 0 :(得分:1)

C ++ 11使用extern告诉编译器实例化模板,语法使用的是具体类型,与问题中建议的语法不同: / p>

extern template struct Point<2>; 

使用C ++ 03,编译器必须在翻译单元中观察Point<2>时实例化模板,并且在与extern关键字结合使用C ++ 11时,它必须知道该模板。

对于您的问题,您在C ++ 03中所做的是将模板的定义分离为单独的头文件(带有cpp后缀,见下文),并且该方法仍然适用于C ++ 11:

#include "point.ipp"

extern template struct Point<2>; // instantiated elsewhere

int main(int, char**) {
    Point<2> p;
    p.foo();
}

主观上,我也不喜欢模板头文件的cpp后缀,以至于我想引起你的注意。这是令人困惑和误导的,尤其是当一个人看到另一个cpp文件中包含的cpp文件时。

考虑分别使用ippixx作为文件扩展名,加上hpphxx,更明确的是file包含特定模板的定义/实现。

答案 1 :(得分:1)

  

我们的方法是有效的C ++(03或11)代码还是我们幸运的是它有效?

您的使用是有效的,但不是规范的。更常见的C ++ 98样式会将显式实例化定义与实现一起分组到point.cpp文件中。

// point.h

template <int dim> struct Point {
  ...
  void foo();
  ...
};

// point.cpp

#include "point.h"

template <int dim>
void Point<dim>::foo() {
  ...
}

// Now the implementation is available, so this is the time to use it:
template struct Point<2>;
template struct Point<3>;
  

使用C ++ 11,我们能够通过在point.cpp中加入main.cpp并声明extern template <int dim> struct Point;来实现同样的目标吗?

您永远不应该#include .cpp个文件。规范样式是将函数模板实现放在头文件中,可以直接放在point.h中,也可以放在名为的模板实现文件中。 point.hpp

C ++ 11增加的好处是你可以拥有快速的编译时间,也可以通过隐式特化使用Point<4>。但是,将实现放在具有extern特化的标头中不会加快编译Point<2>Point<3>

你无法说出extern template <int dim> struct Point;。标题需要列出所有专业化:

extern template struct Point<2>;
extern template struct Point<3>;

所有专门化委托给其他地方是export模板的预期目的。这是一个C ++ 98特性,事实证明它很难实现,因此它已从C ++ 11标准中删除。

答案 2 :(得分:0)

您的方法是有效的C ++代码,并且应该在C ++ 03和C ++ 11中都有效。它被称为显式实例化:

template struct Point<2>;

在应用于类模板时,它显式实例化当前转换单元中某个模板参数列表的类模板的所有成员。使用这种方法,可以很容易地创建模板库,其中可以预先知道一组可能的模板参数(例如,当Point可以是2D和3D而不是1D,4D等时)。< / p>

在C ++ 11中添加显式实例化指令关键字extern

extern template struct Point<2>;

它成为宣言,而不是定义。这种事情的行为类似于变量的通常extern关键字。显式模板实例化声明可以通过以下方式与显式实例化一起使用:

// point.h

template <int dim> struct Point {
  ...
  void foo();
  ...
};

extern template struct Point<2>;
extern template struct Point<3>;

#include "point.hpp"


// point.hpp

#include "point.h"

template <int dim>
void Point<dim>::foo() {
  ...
}

// point.cpp

#include "point.hpp"

template struct Point<2>;
template struct Point<3>;

使用此类代码可以获得与您的代码相同的结果,但另外您允许代码的用户使用Point<1>Point<4>以及Point类模板的其他专精。他们要。没有extern template struct Point<2>;extern template struct Point<3>;指令Point类模板将在用户代码中隐式实例化,即使模板参数23也会降低显式实例化的含义(template struct Point<2>;template struct Point<2>;)。