【问题标题】:Separating definition/instantiation of template classes without 'extern'分离没有'extern'的模板类的定义/实例化
【发布时间】:2014-03-22 09:40:02
【问题描述】:

(不再那么新的)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>;

ma​​in.cpp:2D和3D点的使用

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

现在我想知道:

  • 我们的方法是有效的 C++(03 或 11)代码还是我们只是幸运地成功了?
  • 使用 C++11,我们是否能够通过在 main.cpp 中包含 point.cpp 并声明 extern template &lt;int dim&gt; struct Point; 来实现相同的目标?

【问题讨论】:

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


    【解决方案1】:

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

    extern template struct Point<2>; 
    

    对于 C++03,只要在翻译单元中观察到 Point&lt;2&gt;,编译器就必须实例化模板,而对于 C++11,它知道它必须与 extern 关键字结合使用。

    对于您的问题,您在 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,更清楚的是,该文件包含特定模板的定义/实现。

    【讨论】:

      【解决方案2】:

      我们的方法是有效的 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,我们是否能够通过在 main.cpp 中包含 point.cpp 并声明 extern template &lt;int dim&gt; struct Point; 来实现相同的目标?

      您永远不应该 #include .cpp 文件。规范的风格是将函数模板实现放在一个头文件中,或者直接放在point.h 中,或者放在一个名为例如的模板实现文件中。 point.hpp.

      C++11 带来的好处是编译速度快,还可以通过隐式特化使用Point&lt;4&gt;。但是,将实现放在带有extern 特化的标头中不会加快编译Point&lt;2&gt;Point&lt;3&gt;

      你不能直接说extern template &lt;int dim&gt; struct Point;。标题需要列出所有特化:

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

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

      【讨论】:

        【解决方案3】:

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

        template struct Point<2>;
        

        在应用到类模板时,它显式地为当前翻译单元中的某个模板参数列表实例化类模板的所有成员。使用这种方法,可以很容易地创建模板库,其中可能的模板参数集是预先知道的(在您的情况下,Point 可以是 2D 和 3D,但不能是 1D、4D 等)。

        在 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&lt;1&gt;Point&lt;4&gt;Point 类模板的其他特化。如果没有extern template struct Point&lt;2&gt;;extern template struct Point&lt;3&gt;; 指令,Point 类模板将在用户代码中隐式实例化,即使对于模板参数23,这会降低显式实例化的含义(template struct Point&lt;2&gt;;template struct Point&lt;2&gt;;)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-09-29
          • 1970-01-01
          • 2016-10-11
          相关资源
          最近更新 更多