[C++]模板总结
目录
定义
template开始,后跟一个模板参数列表(在模板定义中,模板参数列表不能为空)
typename是用来定义模板参数关键字(C++98之后),也可以 使用class(切记:不能使用struct代替class)
实例化
隐式实例化:让编译器根据实参推演模板参数的实际类型,编译器一般不会进行类型转换操作,形参和实参类型必须完全匹配。
显式实例化:在函数名后的<>中指定模板参数的实际类型。如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错。
非类型模板参数
用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用
- 浮点数、类对象以及字符串不可作为非类型模板参数
- 非类型的模板参数必须在编译期就能确认结果
模板参数的匹配原则
对于非模板函数和同名函数模板,如果其他条件都相同,在隐式实例化时会优先调用非模板函数,不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板。
函数模板特化
// 必须要先有一个基础的函数模板 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找不到对应函数的地址。
有两种方法可以解决:
- 将声明和定义放到一个文件 “xxx.hpp” 里面或者xxx.h其实也是可以的
- 模板定义的位置显式实例化,这种方法不推荐使用
总结
模板复用了代码,更快迭代开发,增强了代码的灵活性,但是会导致代码膨胀问题,编译时间变长,出现模板编译错误时,不易定位错误。
- c++模板的一些知识点总结
- C++ 模板学习总结(三)模板参数的三种形式
- 【c++】模板知识总结
- c++模板总结
- C++模板template用法总结
- C++模板template用法总结
- C++模板学习总结
- C++模板template用法总结
- STL + c++ + 模板 + 重要思维 + 基础算法+ 经典算法 + 经典实例 + 编程总结+ 心得+ 入门必会 + 知识点汇总。
- C++模板总结
- C++模板总结
- 对c++模板的总结
- C++ 模板使用总结
- C++模板知识总结
- C++模板总结
- [置顶] Nobleman__ ACM 比赛模板 (C++ && Java)个人总结 (不断更新) (自用)
- C++面试总结(三)模板与泛型编程
- 【C++基础学习】引起类模板被实例化情形总结
- 学习总结: C++ 模板 (thinking in C++)
- C++模板知识总结