C++模板类声明和定义几种写法

为什么模板类的实现放在cpp会出错

在编译用到了模板类的编译单元时,编译器需要访问方法或者类的实现,以实例化它们。 如果这些实现不在头文件中,则它们将不可访问,因此编译器将无法实例化模板,进而会导致编译出错。

模板类的定义必须放在.h文件中吗

答案是否定的,模板类的几种写法

声明和定义都放在.h中

// Foo.h
template <typename T>
struct Foo
{
    void doSomething(T param) {}
};

声明和定义分隔开

// Foo.h
template <typename T>
struct Foo
{
    void doSomething(T param);
};

#include "Foo.tpp"

// Foo.tpp
template <typename T>
void Foo<T>::doSomething(T param)
{
    //implementation
}

实现放在了Foo.tpp,然而还是include Foo.tpp在了头文件中,只是做了文件的分隔,但是其他文件include了头文件后,还是把实现也包含进去了,跟都放在.h效果类似,只是实现和声明分开,代码可读性更好

声明放在.h文件,定义放在.cpp文件

// Foo.h
// no implementation
template <typename T> struct Foo { ... };

//----------------------------------------    

// Foo.cpp
// implementation of Foo's methods

// explicit instantiations
template class Foo<int>;
template class Foo<float>;
// You will only be able to use Foo with int or float

正在的声明和实现分离,include了头文件之后不会把实现的代码也加入到该编译单元。然后需要将所有用到的类型都显式的实例化

什么是实例化

编译器怎么处理模板呢?本质上来说,模板可被编译器产生多种多样函数和类的代码。只有当被使用的时候,编译器才会产生用于这种类型的代码。模板不是直接编译成以后需要的各种类的实现,而是将模板代码解析放入内存中,当编译某个编译单元需要用到该模板,则会按需产生相应的代码。实例化是编译器确定特定模板与特定参数集一起使用并在模板上执行参数替换以生成要编译的类或函数以及最终编译为模板的二进制代码的过程。

有两种类型模板实例化,隐式和显式。
显式的实例化见第三段代码,Foo.cpp中显式实例化了Foo和Foo。显式实例化会实例化所有的成员函数。
隐式实例化是一种按需实例化。当你使用一个模板类时,编译器才会进行实例化。例如如果你使用vector,此时编译器才会创建一个vector类型,并且只会实例化所需要的函数。不实例化所有的成员函数主要有两点原因:
1、节省编译时间
2、不同的成员函数对类型属性有一些要求,不实例化可以使得更多的类型可以用到模板类。例如map的operator[]操作符要求value是有默认构造函数的,因为你通过不存在的key访问value的时候会新建一个value,且是需要调用默认构造函数。如果你不需要这个操作,用find和insert也可以达到目标,这样对value的类型就少了一个限制,使得模板类更具有普适性。

特化template时遇到的duplicate symbol问题

stackoverflow上面有案例,详见multiple definition of template specialization when using different objects

Intuitively, when you fully specialize something, it doesn’t depend on a template parameter any more – so unless you make the specialization inline, you need to put it in a .cpp file instead of a .h or you end up violating the one definition rule as David says. Note that when you partially specialize templates, the partial specializations do still depend on one or more template parameters, so they still go in a .h file.

翻译过来:
直观地说,当你完全特化某些东西时,它不再依赖于模板参数,所以除非你使内联专业化,你需要将它放在.cpp文件中而不是.h中,否则你最终会违反大卫说的一个定义规则。 请注意,当您对模板进行部分特化时,部分特化仍然依赖于一个或多个模板参数,因此它们仍然位于.h文件中。
推荐的写法为:
举例,template中static成员的初始化,完全特化实现在.cpp中

template<> int B<A, 1>::a[1] = { };

声明在.h中

template<> int B<A, 1>::a[1];

详见static member initialization for specialized template class

参考文档

Why can templates only be implemented in the header file?
Why can’t I separate the definition of my templates class from its declaration and put it inside a .cpp file?
c++ 模板类 声明和定义都放在.h文件的原因
Meaning of ‘instantiation’ with respect to templates

展开阅读全文

没有更多推荐了,返回首页