什么是C++中用于分离标头/源的模板特化的强大方式

Jak*_*ker 6 c++ declaration instantiation template-specialization

在中等大小甚至大型复杂项目中,分离模板声明和定义对于减少编译时间非常有用.

但是,在复杂的代码中,小程序员的错误可能会导致未被注意的行为更改,例如调用泛型版本而不是专门化.

示例:由于遗漏声明,模板专业化变得不可见.

///////////////////// file  A.hpp /////////////////////

#include <iostream>

template <typename T>

class A 
{
public:
  void foo() 
  {
      std::cerr << " calling generic foo " << std::endl ;
  }
};

// forgetting following declaration leads to an unintended program behaviour
template <> void A< int >::foo();

///////////////////// file  A-foo-int.cpp /////////////////////
#include "A.hpp"

template <> 
void A< int >::foo()
{
  std::cerr << "calling  <int> version of foo" << std::endl;
}

///////////////////// file  main.cpp /////////////////////
#include "A.hpp"

int main(int argc , char** argv)
{
  A<int>* a = new A<int>();
  a->foo();
  return 0;
}

///////////////////// Makefile /////////////////////
CC = g++
CPPFLAGS += -Wall -O3
CXXFLAGS += --std=gnu++0x

all: nonrobust-template-setup

nonrobust-template-setup: main.o A-foo-int.o  
    $(CC)  $(CPPFLAGS) main.o A-foo-int.o  -o nonrobust-template-setup

clean: 
    rm -rf *.o nonrobust-template-setup

//////////////////////////////////////////
Run Code Online (Sandbox Code Playgroud)

问题:可能是更强大的设置(编译器和平台无关),如果,它会是什么样子?

如果没有,测试所需函数版本的好方法是什么?

And*_*owl 2

您不能以这种方式分离声明和定义:如果您将专用成员函数的定义放在单独的.cpp文件中,无论您是否在主模板之后立即声明您的专用化,编译器都将无法实例化它,并且链接器会抱怨未解析的引用。

通常,类模板的成员函数的定义位于头文件中,除非您为相应的类模板提供显式实例化:

template class X<int>; // You should add this to make your program build,
                       // or instantiate the corresponding class template
                       // specialization and invoke the foo() method in the
                       // same translation unit (A.cpp)
Run Code Online (Sandbox Code Playgroud)

一般来说,除非您面临非常可怕的编译时间问题,否则我建议您遵循常见做法,并将所有内容放入头文件中,以便由需要使用类模板的所有翻译单元包含:

///////////////////// file  A.hpp /////////////////////

#include <iostream>

template <typename T>

class A 
{
public:
    void foo() 
    {
        std::cerr << "error: called generic foo " << std::endl ;
    }
};

template <> 
void A< int >::foo()
{
    std::cerr << "calling  <int> version of foo" << std::endl;
}

///////////////////// file  main.cpp /////////////////////
#include "A.hpp"

int main(int argc , char** argv)
{
    A<int>* a = new A<int>();
    a->foo();
    return 0;
}   
Run Code Online (Sandbox Code Playgroud)

如果您面临非常可怕的编译时间问题,那么您可以将成员函数定义分开,并将它们放入具有显式实例化的单独翻译单元中,但在 C++11 中,没有干净/简单的方法来确保您的所有专业化单独文件中的 relegate 在.cpp主模板之后立即声明(正如良好实践所建议的那样)。如果有的话,我想它会很受欢迎,你就不需要来这里问它了,因为每个人都面临这样的设计问题。

在某些情况下,一些奇特的宏可能会有所帮助,但令人怀疑的是,在真正复杂的项目中,它们会带来比维护痛苦更多的好处。

C++03标准中尝试通过引入export关键字来解决这个问题,但实现经验证明编译器供应商很难支持,这就是为什么它export不再是C++标准的一部分(自C++11起) 。

希望更好的模块解决方案能够进入 C++14 并为模板设计提供解决方案。