c++ - 什么是 C++ 中用于分离 header /源的模板特化的可靠方法

标签 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

//////////////////////////////////////////

问题: 是一个更健壮的设置可能(独立于编译器和平台) 如果是,它会是什么样子?

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

最佳答案

您不能以这种方式分离声明和定义:如果您将专用成员函数的定义放在单独的 .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)

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

///////////////////// 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;
}   

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

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

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

希望有更好的模块解决方案将其纳入 C++14 并为模板设计提供解决方案。

关于c++ - 什么是 C++ 中用于分离 header /源的模板特化的可靠方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15367369/

相关文章:

c++ - 将 bool 转换为位域中的位

c++ - 我不明白为什么会编译

function - Lua中的方法声明

c++ - 无法在NetBeans IDE中编译C++-给出错误 “CreateProcess error=193, %1 is not a valid Win32 application”

c++ - 在arm-linux-gnueabihf-g++交叉编译期间出错。无法识别共享库文件

c++ - 在lambda中锁定std::shared_ptr的复制操作

c - "int *p =0;"和 "int *p; *p=0;"有什么区别

java - 在 main 方法中实例化内部类

javascript - 我是否在backbone.js 路由中实例化了太多对象?

c# - 为什么 C# 实例构造函数的名称与返回的类型不同?