C++模板编程 -- 类模板

概述

C++中使用类模板一般的格式是:

template<typename T, ...>
class TemplateClass {
    ...
};

其中,第一行的template<…>是声明这是一个模板类。具体实现的时候可能有一个模板参数,也可能有多个,可以有类类型,也可以有非类类型。还可以有默认的模板实参。

带默认模板实参的情形 (Default Template Arguments)

template<typename T, typename V = char>
class C {
    ...
};

上面的代码中typename V = char就是属于有默认模板实参的。

特化与偏特化

模板的偏特化/部分特化/局部特化,中文翻译有很多,英文原文是 Partial Specialization。

特化的原文就是 Specializations

template<typename T, typename T2>
class C {
    ...
};

偏特化 (Partial Specialization)

偏特化上面的模板有两类,其一是:

template<typename T, typename T>
class C {
    ...
};

也就是说正好这两种类型一样。

其二是:

template<typename T, int>
class C {
    ...
};

也就是说某一种模板类型具体化了。

特化 (Specializations)

如果是特化上面的模板类,那么特化的方式之一可以如下:

template<>
class C<std::string, int> {
    ...
};

注意,特化的标志就是template<>。

特化与偏特化在模板匹配的时候优先级要高于模板,一般是要针对一些(被特化的)类型想要做一些特别的事情,比如优化内存管理。

写模板一定会过的坎 – 链接错误

当我们开始写一个类模板的时候,比如:

首先写一个类的头文件:

// class T header file -- foo.h

template<typename T>
class Foo {
public:
    void print_msg();
    ...
};
...

然后写一个源文件:

// class T source file -- foo.cpp

#include "foo.h"

template<typename T>
void Foo<T>::print_msg() {
    
}

然后在另一个地方使用上面的类:

// may be main.cpp

#include "foo.h"

void entry(){
    Foo<int> o;
    o.print_msg();
}

结果在编译的时候发现编译环节没有问题,但是链接的时候发现有错误,提示找不到Foo::print_msg()的定义。

这是因为使用模板的地方只是包含了foo.h文件,但是还没有对Foo模板进行int的实例化,原因下面慢慢道来。

我们一般的类,比如定义了一个SimpleFoo,分开定义在一个头文件,一个源文件中,编译的时候我们能够唯一确定两个文件说的是一个类型,所以链接的时候没有问题。

但是对于模板而言,必须要等到实例化的时候才能确定下具体的类型,比如说Foo和Foo两种类型,即使short可以隐式地转化为int,但是这对于模板来说仍然是两种截然不同的类型。而没有实例化的模板,根本没有任何编译好的代码。所以上面那种出错的情况找不到实现代码也就不奇怪了。一个更加详细的解释参考:Splitting templated C++ classes into .hpp/.cpp files–is it possible? in StackOverflow

解决的办法,一般说是有三种(包含模型):

  1. 将所有代码写在头文件中
  2. 在头文件的结尾include源文件
  3. 在所有引用该模板的地方包含源文件

但是,实践下来发现,第二种方案在MSVC 2015 上编译不过,第三种做法太烂没见过这种做法,只有第一种可行。不过第一种方法会有两个问题:造成编译时间变长,代码体积变得更大。

在具体写带默认模板参数的过程中还发现一个问题:

template<typename T, typename t1 = T::SubType>
class Foo{
    ...
};

上面的代码在MSVC 2015上编译没有问题,但是在Xcode(clang++)上编译出现错误,错误的点是typename t1 = T::SubType。后来更具提示修改为:

template<typename T>
class Foo{
    using t1 = typename T::SubType;
    ...
};

就在两个环境下都正确了。注意一定要在using那句中添加typename标志,不然编译不过。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值