您的位置:首页 > 编程语言 > C语言/C++

[C++]模板总结

2019-08-13 13:16 1436 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/ranjiahao_study/article/details/99205783

目录

定义

template开始,后跟一个模板参数列表(在模板定义中,模板参数列表不能为空)

typename是用来定义模板参数关键字(C++98之后),也可以 使用class(切记:不能使用struct代替class)

实例化

隐式实例化:让编译器根据实参推演模板参数的实际类型,编译器一般不会进行类型转换操作,形参和实参类型必须完全匹配。

显式实例化:在函数名后的<>中指定模板参数的实际类型。如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错。

非类型模板参数

用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用

  1. 浮点数、类对象以及字符串不可作为非类型模板参数
  2. 非类型的模板参数必须在编译期就能确认结果

模板参数的匹配原则

对于非模板函数和同名函数模板,如果其他条件都相同,在隐式实例化时会优先调用非模板函数,不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板。

函数模板特化

// 必须要先有一个基础的函数模板
template<class T>
bool IsEqual(T& left, T& right) {
return left == right;
}

// 关键字template后面接一对空的尖括号<>,函数名后跟一对尖括号,尖括号中指定需要特化的类型
template<>
bool IsEqual<char*>(char*& left, char*& right) {
// 函数形参表必须和模板函数的基础参数类型相同
if (strcmp(left, right) == 0)
return true;
return false;
}

一般情况下为了实现简单通常都是将函数直接给出

类模板

类模板中函数放在类外进行定义时,需要加模板参数列表。

类模板实例化需要在类模板名字后跟<>,实例化的类型放在<>中,类模板名字不是真正的类,而实例化的结果才是真正的类。

类模板特化

template<typename T1, typename T2>
class Data {};

// 全特化
template<>
class Data<int, char> {};

// 部分特化
template<typename T1>
class Data<T1, int> {};

// 限制特化
template<typename T1, typename T2>
class Data<T1*, T2*> {};

类型萃取

实现Copy函数,使其可以拷贝任意类型

单使用memcpy,可以拷贝内置类型,如果涉及到深拷贝(比如string),就会出错。

单使用循环赋值,效率很低

通过增加bool参数判断是否内置类型,就可将两种拷贝的优势结合。但用户需要根据所拷贝元素的类型传递第三个参数,那出错的可能性就增加。

所以需要通过类型萃取来判断是否内置类型,但需要将所有类型遍历一遍,每次比较都是字符串的比较,效率比较低。

bool IsPODType(const char* strType) {
const char* arrType[] = { "char", "short", "int", "long", "long long", "float",
"double", "long double" };
for (size_t i = 0; i < sizeof(arrType) / sizeof(arrType[0]); ++i) {
if (strcmp(strType, arrType[i]) == 0)
return true;
}
return false;
}

template<class T>
void Copy(T* dst, const T* src, size_t size) {
if (IsPODType(typeid(T).name())) // RTTI
memcpy(dst, src, sizeof(T)*size);
else {
for (size_t i = 0; i < size; ++i)
dst[i] = src[i];
}
}

一般写法:

// 内置类型
struct TrueType {
static bool Get() { return true; }
};

// 代表自定义类型
struct FalseType {
static bool Get() { return false; }
};

template<class T>
struct TypeTraits {
typedef FalseType IsPODType;
};

template<>
struct TypeTraits<char> {
typedef TrueType IsPODType;
};
template<>
struct TypeTraits<int> {
typedef TrueType IsPODType;
};
// ... 将所有内置类型都特化

template<class T>
void Copy(T* dst, const T* src, size_t size) {
if (TypeTraits<T>::IsPODType::Get()) // RTTI
memcpy(dst, src, sizeof(T)*size);
else {
for (size_t i = 0; i < size; ++i)
dst[i] = src[i];
}
}

简便写法:

struct TrueType {};
struct FalseType {};

template <class T>
struct TypeTraits {
typedef FalseType IsPodType;
};

template<>
struct TypeTraits<char> {
typedef TrueType IsPODType;
};
template<>
struct TypeTraits<int> {
typedef TrueType IsPODType;
};
// ... 将所有内置类型都特化

template <class T>
void _Copy(T* dst, T* src, size_t size, TrueType) {
memcpy(dst, src, sizeof(T)* size);
}

template <class T>
void _Copy(T* dst, T* src, size_t size, FalseType) {
for (size_t i = 0; i < size; ++i) {
dst[i] = src[i];
}
}

template <class T>
void Copy(T* dst, T* src, size_t size) {
_Copy(dst, src, size, TypeTraits<T>::IsPodType()); // RTTI
}

模板分离编译

如果将模板的声明放在.h文件中,将其定义放在其他.cpp中,在main.cpp中实例化就会出现链接错误。

因为头文件不参与编译,编译器对工程中的多个源文件单独编译,然后将多个obj文件链接。

在定义的.cpp文件中编译器找不到对模板的实例化,不会生成对应的函数。

所以在main.cpp中call找不到对应函数的地址。

有两种方法可以解决:

  1. 将声明和定义放到一个文件 “xxx.hpp” 里面或者xxx.h其实也是可以的
  2. 模板定义的位置显式实例化,这种方法不推荐使用

总结

模板复用了代码,更快迭代开发,增强了代码的灵活性,但是会导致代码膨胀问题,编译时间变长,出现模板编译错误时,不易定位错误。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: