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)
问题:可能是更强大的设置(编译器和平台无关),如果,它会是什么样子?
如果没有,测试所需函数版本的好方法是什么?
您不能以这种方式分离声明和定义:如果您将专用成员函数的定义放在单独的.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 并为模板设计提供解决方案。