Home

cuipf blog

15 Aug 2017

C++11特性介绍——模板相关

模板别名

在C++ 我们经常使用typedef为类型定义别名, 遇到一些较长类型名字, 尤其在使用模板和域的时候, 使用别名的优势更加的明显;

在C++ 11中, 定义别名已经不是typedef 的专属能力了, 也可以使用using来完成; 如下:

//使用using 定义别名
using uint = unsigned int;
typedef unsigned int UINT;
std::cout << is_same<uint, UINT>::value << std::endl;  //output: 1

在模板编程的时候, using的语法甚至比typedef更加灵活, 使用typedef则很难达到这种效果:

template<typename T>
using MapString = std::map<T, char*>;
MapString<int> num_strings;

带默认参数的模板

C++ 11中引入了类似于函数默认形参的样式的,带默认参数的模板; 使用如下:

/*
*	模板函数的默认模板参数
*/

template<typename T1, typename T2 = int>
class DefClass1;
template<typename T1 = int, typename T2>
class DefClass2;						  //编译错误
template<typename T, int i = 0>
class DefClass3;
template<int i = 0, typename T>
class DefClass4;				           //编译错误

template<typename T1 = int, typename T2>
void DefFunc(T1 a, T2 b);
template<int i = 0, typename T>
void DefFunc(T a);

说明:

  • 类模板的默认参数必须按照从右往左定义(如上);
  • 对于带默认参数的函数模板,默认参数位置随意;

外部模板

问题:

同一个函数模板,如果在两个文件中使用即实例化,编译器会对每一文件实例化一份,产生大量的冗余代码,而且在链接的时候,链接器会通过一些编译器辅助手段将重复实例化的代码删掉,会增加编译器的编译时间和连接时间;

解决:

解决方法如同使用外部变量(extern)一样;显示的实例化再加外部模板声明就可以很好地解决这一问题,

例如:对以下模板:

template <typename T> void fun(T) {}

我们只需要声明:

template void fun<int> (int);

这样编译器就会在本编译单元实例化出一个fun<int>(int)版本的函数。C++ 11中又加入了外部模板的声明,语法如下:

extern template void fun<int> (int);

外部模板的使用如下方式如下:

在test1.cpp中做显示的实例化:

#include "test.h"
template void fun<int> (int);
void test1() { fun(3) ; }

在test2.cpp中做外部模板的说明:

#include "test.h"
extern template void fun<int> (int);
void test1() { fun(3) ; }

这样在test2.o中不会再生成fun<int> (int)的实例代码。

说明

  • 外部模板和外部变量相比,如果不使用外部模板并不会导致任何错误,外部模板更应该说是一种优化编译时间和空间的优化方法。
  • 千万不要低估模板实例化的开销, 特别是项目中包含大量模板!

局部和匿名类型作为模板实参

在C++ 98中局部的变量、匿名的类型以及匿名类型的变量都是无法作为模板的实参,当然不能作为模板的实参的还有,匿名的联合体以及枚举类型;

这些看起来都是不必要的限制,在C++ 11中等到了改善(c++11支持了这些用法);见代码:

/*
	局部和匿名类型作模板实参
*/

template <typename T>
class X { };
template <typename T>
void TempFun(T t){};
struct A{ } a;
struct {int i;} b;           //b为匿名类型变量
typedef struct {int i; } B;  //B为匿名类型

void test_template()
{
	struct C
	{

	} c;
	X<A> x1;		//C++98 通过 C++11通过
	X<B> x2;		//C++98 错误 C++11通过			
	X<C> x3;		//C++98 错误 C++11通过
	TempFun(a);		//C++98 通过 C++11通过
	TempFun(b);		//C++98 错误 C++11通过
	TempFun(c);		//C++98 错误 C++11通过
}

cuipf

scribble