模板与泛型编程
- 模板概念、函数模板定义、调用:
- 各种函数:
- 替换失败不是一个错误`SFINAE`(substitution failure is not an error):
- 由来:
- 特性:
- *c++11引入的类模板`enable_if`,体现了`SFINAE`的特性:
- `std::enable_if`或`std::enable_if_t`,作为函数模板的返回类型
- `std::enable_if` 或 `std::enable_if_t`,作为类模板中构造函数模板模板参数列表的一员
- typename使用场合:
- 模板参数的类型:
- 类模板中可变参展开的几种继承方式:
- `myClass<Args...>`继承:
- `myClass<Args>...`继承:相当于一个派生类,继承多个基类
- `myClass<Args,char>...`继承:
- `myClass<Args, Args...>...`继承:
- 函数模板:
- 函数模板会被编译两次:
- 函数调用时,根据调用时的函数实参,推断出来的模板参数;否则,则需要用<>提供模板参数:
- 函数模板全特化(函数模板不能偏特化):
- 普通(重载的)函数、函数模板的全特化版本的区别:
- 注意:
- 函数指针:
- 作为其他函数的参数(将函数指针定义为函数指针类型):
- 作为函数模板参数:
- 结合decltype关键字,实现函数后置返回类型:
- 非类型模板参数(必须编译时就能确定):
- 类模板:
- 类模板的定义和实例化:
- 拷贝构造函数模板、拷贝赋值运算符模板:
- 应用:用模板类主,简单实现STL中的容器类
- 静态数组array:
- 动态数组vector:
- 栈stack:
- 嵌套和递归使用模板类:
- 类模板特化:
- 前言:
- 类模板的全特化:
- 类模板的偏特化:
- 根据模板参数的数量,进行局部偏特化:
- 根据模板参数的范围,进行局部偏特化:
- 模板类与继承:
- 模板类继承普通类(常见):
- 普通类继承模板类(常见):
- 情况一:普通类要继承模板类需将自己先变成模板类
- 情况二:普通类继承模板类的实例版本
- 模板类继承模板类:
- 模板类用于“函数参数和返回值”的三种形式:
- 普通函数,参数和返回值类型是模板类的实例化版本
- 函数模板,参数和返回值是某种的模板类
- 函数模板,参数和返回值是任意类型(支持普通类、模板类和其他类型)
- 类模板中的友元:
- 模板类与友元函数:
- *非模板友元:
- **约束模板友元**:
- 非约束模板友元:
- 模板类与友元类:
- 情况一:类模板的某个实例成为友元类
- 情况二:类模板成为友元类模板
- 情况三:类型模板参数成为友元类
- 模板模板参数:(将模板类用作模板参数)
- 可变参数模板(允许模板定义中,含有0~任意个模板参数):
- 可变参函数模板:
- 若模板函数中,有一个固定参数,其他是可变参数,则如何展开参数包?
- 举例,printf实现:
- *可变参类模板:
- 通过“递归继承”的方式,展开参数包:
- 通过“递归组合”的方式,展开参数包:
- (多个)基类的参数包的展开:
- 可变参数模板的各种特化版本:
- using用来定义类型(类模板)时,包含了typedef的所有功能:
- using用来给“类型模板”起别名:
- 定义类型模板,是一个函数指针模板:
- typedef用来给“类型”起别名:
- 定义函数指针类型:
- 万能引用、完美转发:
- 模板参数类型推导、查看类型推断结果:
- 模板参数类型推导:
- 指针T*或引用类型T&:
- 万能引用T&&:
- *传值方式T:
- **T形参类型,会将实参的引用、const丢弃**。
- **T形参类型+传入`std::ref()`或`std::cref()`(对象包装器)**,使用:
- *引用折叠(reference-collapsing rules):
- 函数/类模板的分文件编写:
- 模板总结:
- 优点:
- 缺陷:
- 泛型编程:
- c++标准库中,常见的函数模板、类模板、别名模板等功能组件:
- std::declval() 函数模板:
- 功能:
- declval的使用细节:
- 返回类型本身是不好的;
- 返回左值引用,还是返回右值引用;
- 引用限定符修饰的成员函数;
- 推导(可变参数)函数模板返回类型;
- std::add_rvalue_reference 类模板:
- std::true_type、std::false_type 类类型:
- 一般用作基类被继承,这样派生类就具有了“真 或 假”的意味:
- 可作为返回类型使用:
- std::void_t别名:
- 判断类中,是否存在某个类型别名:
- 判断类中,是否存在某个成员变量:
- 判断类中,是否存在某个成员函数:
- std::is_copy_assignable类模板:
- 综合举例:重载+运算符,完成两个不同类型vector容器元素求和
- std::conditional类模板:
- 定义:
- 举例:conditional嵌套,实现复杂分支逻辑
- std::function类模板(“可调用对象包装器”):
- 使用细节:
- 自定义`_nmsp::function`类模板:完成可调用对象的包装和调用
- std::remove_all_extents类模板:
- std::integer_sequence类模板:
- 功能:
- `std::make_integer_sequence`为别名模板:
- 自定义“正序”_nmsp::Integer_Sequence类模板:
- 自定义“逆序”_nmsp::Integer_Sequence_Reverse类模板:
- 将一个数字重复多次生成一个类型Repeat_Integer:
- std::is_union、std::is_class、std::integral_constant类模板:
- 萃取 trait(大量使用在“模板与泛型编程”、“元编程”中):
- fixed traits 固定萃取:
- 常规类型的萃取,举例:
- 迭代器萃取:
- 推导过程详解:“容器类 --> 迭代器类 --> 迭代器的种类”,以list为例:
- 通过容器(数组)类型萃取元素类型:
- GetElementType类模板(泛化和多个特化版本),来完成该功能:
- PrintElementType函数模板,来完成该功能:
- 优化GetElementType类模板:
- 引用类型的移除和增加:
- 引用类型移除`remove_reference`类模板或`remove_reference_t`变量(别名)模板:
- 自定义实现引用移除的功能:
- 引用类型增加`add_lvalue_reference`类模板、`add_rvalue_reference`类模板:
- 自定义实现引用增加的功能:
- std::remove_const类模板和自定义实现const修饰符的去除:
- 退化decay技术:
- 自定义实现“const”和“&/&&”修饰符去除:
- 自定义实现“数组类型 --退化为--> 指针类型”:
- 自定义实现“函数名 --退化为--> 函数指针”:
- value traits值萃取:
- value_traits常规范类:
- 数组/容器中,通过“静态成员函数”,解决元素的初始化:
- std::is_void类模板,判断传入的是否为void类型:
- `std::is_same`类模板,判断传入的两个类型是否相等:
- 使用了SFINAE特性的信息萃取:
- 用成员函数重载实现`is_default_constructible`:
- 用类模板特化实现`is_default_constructible`:
- 用成员函数重载实现`is_convertible`:
- 用成员函数重载实现`is_class`:
- 用成员函数重载实现`is_base_of`:
- 迭代器萃取:
- 策略 policy 技术中的算法策略:
- 常规范例:
- 普通策略类:
- 策略类模板:
- 萃取 trait 技术与策略 policy 技术的比较:
模板概念、函数模板定义、调用:
模板是泛型编程的基础,模板支持将类型作为参数的程序设计方式。
各种函数:
- 对于给定的函数名,可以有普通函数、函数模板、特化的函数模板、以及它们的重载版本。
- 必须先有泛化版本,才能有特化版本(特化版本,编译器会优先选择)。
- 编译器使用各种函数的规则:
- 编译器选择最合适的调用顺序:普通函数、特化版本的函数、泛化版本的函数;
- 如果函数模板能够产生更好的匹配,将优先于非模板函数;
替换失败不是一个错误SFINAE
(substitution failure is not an error):
- SFINAE主要是针对“函数模板重载”,在编译器内部需要遵循的一种重要原则。
由来:
当函数模板匹配失败后,并不会报错,而是寻找更合适的函数或模板,如果还找不到则会报错(未找到匹配的重载函数)。
特性:
(编译器)虽然看不出(实例化的模板)的对错(错误一般指无效的类型、表达式等),但可决定是否选择你。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;namespace _nmsp
{template <typename T>typename T::size_type func(const T& t){return t[0] * 2;}
}int main()
{// vector<int>::size_type是存在的,故可以正常的调用"typename T::size_type func(const T& t)"vector<int> vctor(10, 1);vector<int>::size_type val = _nmsp::func(vctor);cout << val << endl;for_each(vctor.begin(), vctor.end(), [](int val) { cout << val << ","; });// Error : int::size_type是不存在的int i = 0;//_nmsp::func(i); return 0;
}
*c++11引入的类模板enable_if
,体现了SFINAE
的特性:
用来辅助其他模板的设计,表现一种:编译器的分支逻辑(编译期就可以确定走哪条分支)。
#include <iostream>
using namespace std;namespace _nmsp
{/* enable_if类模板的原码分析:*///template <bool _Test, typename _Ty = void> //class enable_if {};//template <typename _Ty = void> //class enable_if<true, _Ty> //{//public:// using type = _Ty;//};
}int main()
{std::enable_if<(3>2)>::type* ptr = nullptr; // 等价于 void* ptr = nullptr;return 0;
}
std::enable_if
或std::enable_if_t
,作为函数模板的返回类型
#include <iostream>
using namespace std;namespace _nmsp
{template <typename T>typename std::enable_if<(sizeof(T) > 2)>::type func1() { cout << "std::enable_if<(sizeof(T) > 2)>::type func()" << endl; }/* enable_if_t 的原码分析:*///template <typename _Test, typename _Ty=void>//using std::enable_if_t = typename std::enable_if<_Test, _Ty>::type;template <typename T>std::enable_if_t<(sizeof(T) > 2)> func2() { cout << "std::enable_if_t<(sizeof(T) > 2)> func()" << endl; }
}int main()
{// std::enable_if<>::type 作为返回值类型_nmsp::func1<int>();//_nmsp::func1<char>(); //Error:编译器并没有在enable_if的泛化版本中,找到对应的type,故编译失败// std::enable_if_t<> 作为返回值类型_nmsp::func2<int>();return 0;
}
std::enable_if
或 std::enable_if_t
,作为类模板中构造函数模板模板参数列表的一员
避免本应调用拷贝构造,而误调用了构造函数模板,造成报错。
#include <iostream>
#include <string>
using namespace std;class Human
{
public:/* Human的构造函数: *//*// 初始化列表中,会调用string(const string& str)的拷贝构造函数Human(const string& name) : _name(name){cout << "Human(const string& name)" << endl;}// 右值传入后,name会变成左值;std::move()只会将左值转换为右值;// 初始化列表中,会调用string(string&& str)的移动构造函数Human(string&& name) : _name(std::move(name)){cout << "Human(string&& name)" << endl;}*/// 构造函数的完美转发: /*//但这里传入的只能是string相关的类型,避免被误看作Human的移动构造函数template<typename T>Human(T&& name) : _name(std::forward<T>(name)){cout << "template<typename T> Human(T&& name)" << endl;}*///解决方案:通过std::enable_if_t<bool, _Ty=void>解决,即bool值为“T == string相关类型”是否成立template <typename T, typename = std::enable_if_t<std::is_convertible<T, std::string>::value>>Human(T&& name) : _name(std::forward<T>(name)){cout << "template<typename T, typename = std::enable_if_t<std::is_convertible(T, string)::value>> Human(T&& name)" << endl;}/* Human的拷贝构造函数: */Human(const Human& human) : _name(human._name){cout << "Human(const Human& human)" << endl;}/* Human的移动构造函数: */Human(Human&& human) : _name(std::move(human._name)){cout << "Human(Human&& human)" << endl;}
private:string _name;
};int main()
{/* 构造函数: */Human human1(string("hi"));string name = "hi";Human human2(name);cout << "......................." << endl;/* 拷贝构造函数: *///Error:受到构造函数中的函数模板的影响,不能正常地调用到拷贝构造函数//Human human3(human2);//解决方案:通过std::enable_if<bool, _Ty>解决,即传入的条件为“_Ty == string相关类型”Human human3(human2); // 这里由于enable_if_t中bool为false,故根据SFINAE特性,编译器会直接忽略,并寻找到下一个匹配的函数,即拷贝构造函数cout << "......................." << endl;const Human human4(string("hi"));// 因human4 : const Human类型,故能正常地调用到拷贝构造函数Human human5(human4);cout << "......................." << endl;/* 移动构造函数: */// 不受到构造函数中的函数模板的影响,能正常地调用到移动构造函数Human human6(string("hi"));Human human7(std::move(human6));return 0;
}
其中,std::is_convertible<_From, _To>
,用来判断是否能从_From
类型隐式转换为_To
类型,返回为bool值。
cout << "int => float : " << is_convertible<int, float>::value << endl;
cout << "string => int : " << is_convertible<string, int>::value << endl;
typename使用场合:
-
在模板定义中,表明其后的模板参数是类型参数;
函数模板建议使用typename描述通用数据类型,类模板建议用class描述。
-
使用类的类型成员,typename用来标识变量是一个类型;
-
::
作用域运算符,可以用来访问静态成员,也可以用来访问类成员;
// 函数模板
template<typename T>
typename T::size_type getLength(const T &str)
// typename T::size_type,代表一个类型名
{if (str.empty()){return 0;}return str.size();
}void test11_4()
{string str = "test";string::size_type size = getLength(str); // 等价于size_t size = str.size();// 其中,using string::size_type = size_tcout << "size = " << size << endl;
}
模板参数的类型:
模板参数的类型:类型模板参数、非类型模板参数、模板模板参数(函数模板不能使用模板模板参数)、可变参数(任意数量、任意类型,主要通过“递归继承”、“递归组合”来实现参数包展开)。
类模板中可变参展开的几种继承方式:
myClass<Args...>
继承:
#include <iostream>
using namespace std;template <typename... Args>
class MyClass1
{
public:MyClass1() { cout << "MyClass1()的参数包个数:" << sizeof...(Args) << endl; }
};template <typename... Args>
class MyClass2 : public MyClass1<Args...>
{
public:MyClass2() { cout << "MyClass2()的参数包个数:" << sizeof...(Args) << endl; }
};int main()
{MyClass2<int, float, char> myclass2; return 0;
}
myClass<Args>...
继承:相当于一个派生类,继承多个基类
#include <iostream>
using namespace std;template <typename... Args>
class MyClass1
{
public:MyClass1() { cout << "MyClass1()的参数包个数:" << sizeof...(Args) << endl; }
};template <typename... Args>
class MyClass2 : public MyClass1<Args>...
{
public:MyClass2() { cout << "MyClass2()的参数包个数:" << sizeof...(Args) << endl; }
};int main()
{MyClass2<int, float, char> myclass2; return 0;
}
myClass<Args,char>...
继承:
#include <iostream>
using namespace std;template <typename... Args>
class MyClass1
{
public:MyClass1() { cout << "MyClass1()的参数包个数:" << sizeof...(Args) << endl; }
};template <typename... Args>
class MyClass2 : public MyClass1<Args, char>...
{
public:MyClass2() { cout << "MyClass2()的参数包个数:" << sizeof...(Args) << endl; }
};int main()
{MyClass2<int, float, double> myclass2; return 0;
}
myClass<Args, Args...>...
继承:
#include <iostream>
using namespace std;template <typename... Args>
class MyClass1
{
public:MyClass1() { cout << "MyClass1()的参数包个数:" << sizeof...(Args) << endl; }
};template <typename... Args>
class MyClass2 : public MyClass1<Args, Args...>...
{
public:MyClass2() { cout << "MyClass2()的参数包个数:" << sizeof...(Args) << endl; }
};int main()
{MyClass2<int, float, double> myclass2; return 0;
}
函数模板:
template<typename T>
T func(T a, T b)
{. . . .
}int result = func(3, 2);
函数模板会被编译两次:
- 实例化前,检查模板代码本身是否有语法错误;
- 实例化期间,检查对模板代码的调用是否合法;
函数调用时,根据调用时的函数实参,推断出来的模板参数;否则,则需要用<>提供模板参数:
- 编译时,编译器会根据实参推导出函数模板的形参类型后,会“实例化”一个特定版本的函数即生成函数定义,这被称为实例化;
- 模板的定义并不会导致编译器生成代码,只有在调用函数模板且编译器实例化一个特定版本的函数之后,才会生成代码;
- 函数模板的定义通常在.h文件中,故编译器在生成代码时,只需在.h文件中找到函数本体;
函数模板全特化(函数模板不能偏特化):
// 函数模板泛化版本
template<typename T, typename U>
void tfunc(T &tmpVal1, U &tmpVal2)
{cout << "tfunc()函数的泛化版本" << endl;cout << tmpVal1 << endl;cout << tmpVal2 << endl;
}// 全特化版本
template<>
void tfunc<const char*, int>(const char* &tmpVal1, int &tmpVal2)
{cout << "tfunc()函数的全特化版本" << endl;cout << tmpVal1 << endl;cout << tmpVal2 << endl;
}// 普通函数
void tfunc(const char* &tmpVal1, int &tmpVal2)
{cout << "tfunc()函数的普通版本" << endl;cout << tmpVal1 << endl;cout << tmpVal2 << endl;
}
普通(重载的)函数、函数模板的全特化版本的区别:
// 普通函数
void tfunc(const char* &tmpVal1, int &tmpVal2)// 全特化版本:相当于实例化了函数模板,而非函数重载
template<>
void tfunc<const char*, int>(const char* &tmpVal1, int &tmpVal2)
在传递字符串给函数模板、函数模板的特化版本时,如果有 数组类型模板参数、指针类型模板参数,编译器会认为 数组类型模板参数比指针类型模板参数更合适。
void test13_3()
{// 函数模板全特化(函数模板不能偏特化)const char *ptr = "I love China!";int i = 12;tfunc(ptr, i); // T: const char * 、 U:int//tfunc("I love China!", 12); // T:const char [14]、U:int
}
注意:
-
可为类成员函数创建模板,但不能是虚函数(动态调用的,不能在编译时推导)和析构函数(没有参数和返回值,故不需要);
-
使用函数模板时,如果是自动类型推导,不会发生隐式类型转换;
如果是显式制定了函数模板的数据类型,则可以发生隐式类型转换;
#include <iostream> using namespace std;template<typename T> T add(T a, T b) {return a + b; }int main() {// 自动类型推导,不会发生隐式类型转换,故会报错//int a = 1; char b = 2;//cout << add(a, b) << endl;// 显式指定了函数模板的数据类型,可以发生隐式类型转换int a = 1; char b = 2;cout << add<int>(a, b) << endl;return 0; }
-
函数模板支持重载,可以有非通用数据类型的参数;
- 函数模板可以像普通函数一样被重载;
- 非模板函数可以和同名的模板函数共存;
编译器通过函数模板参数推到来决定调用哪个函数重载。
-
如果在调用函数模板时,在函数名后加空模板参数列表,则指定只能调用函数模板,不能调用特化版本。
#include <iostream> using namespace std;template<typename T> T add(T t1, T t2) {cout << "T add(T t1, T t2)" << endl;return t1 + t2; }//template<> // 可写可不写 int add(int t1, int t2) {cout << "int add(int t1, int t2)" << endl;return t1 + t2; }int main() {add(10,10); // 调用重载版本add<>(10,10); // 只能调用函数模板return 0; }
函数指针:
作为其他函数的参数(将函数指针定义为函数指针类型):
// 函数指针想要当作函数的参数,就要将函数指针定义为函数指针类型
int add(int i, int j)
{return (i + j);
}// 定义函数指针类型
typedef int (*FuncPtrType)(int, int);
// 等价于using FuncPtrType = int(*)(int, int);void test(int i, int j, FuncPtrType funcPtr)
// funcPtr就是函数指针
{int result = funcPtr(i, j); // 指针类型的变量,相当于调用函数cout << "funcPtr(" << i << ", " << j << ") = " << result << endl;
}// 调用test函数
test(12, 13, add);
作为函数模板参数:
// 函数指针想要当作函数的参数,就要将函数指针定义为函数指针类型
int add(int i, int j)
{return (i + j);
}// 定义函数指针类型
typedef int (*FuncPtrType)(int, int);
// 等价于using FuncPtrType = int(*)(int, int);// 函数模板
template<typename T, typename F = FuncPtrType>
void TestFunc(const T &i, const T &j, F funcPtr = add)
{cout << "funcPtr(" << i << ", " << j << ") = " << funcPtr(i, j) << endl;
}
结合decltype关键字,实现函数后置返回类型:
#include <iostream>
using namespace std;template<typename T1, typename T2>
auto func(T1 x, T2 y) -> decltype(x+y)
// 其中decltype(x+y)只能是后置函数类型,这里的auto不具有类型推断的能力只是后置返回类型语法的一部分。
{decltype(x+y) tmp = x + y;return tmp;
}int main()
{cout << func<int, double>(1,1.2) << endl;cout << func(1,1.2) << endl;return 0;
}
非类型模板参数(必须编译时就能确定):
在模板参数列表中可定义:类型模板参数T、非类型模板参数(size_t / int / … 等整型家族)。
/* 非类型模板参数,用于模板类 */
#include <iostream>
#include <cstring>
using namespace std;// MAX_LEN为非类型模板参数,且可以指定默认值,且模板中不能修改其值(为常量)
template<class T, size_t MAX_LEN=10>
class Array
{
private:T items[MAX_LEN];
public:Array() { memset(items, 0, sizeof(items)); }~Array() { }// 两种可以同时存在:// 1、该重载的运算符[],既可以修改成员变量,又可以访问成员变量T& operator[](int idx){return items[idx];}// 2、该重载的运算符[],只能访问成员变量const T& operator[](int idx) const{ return items[idx];}
};int main()
{ // 使用非类型参数的类模板,在实例化模板时,非类型参数必须使用“常量表达式”Array<int, 10> array;array[0] = 1; array[1] = 2;for (int i = 0; i < 10; ++i){cout << array[i] << ",";}cout << endl;return 0;
}
非类型参数的值,(模板中不能修改非类型参数的值)
- 可为常量表达式(这些常量表达式可通过用户指定,也可通过编译器推断),但推断出的类型只能是整型家族;
- 可以直接使用整型家族作为左值,浮点数、类对象以及字符串是不允许作为非类型模板参数的值;
非类型模板参数,编译器在编译阶段就需要根据传入的非类型模板参数生成对应的类或函数。
缺点:
- 程序中,不同的非通用类型参数将导致编译器生成不同的类;
- 相比非类型模板参数,在构造函数中初始化则更实用,因为可以指定参数的大小,而不是非类型模板参数大小固定(硬编码);
类模板:
类模板的定义和实例化:
类模板的成员函数,可以被写在类模板的定义中(会被隐式声明为inline函数)。如果类模板成员函数定义在类模板之外,则写法是:
template<模板参数列表>
函数返回类型 类名<模板参数列表>::成员函数名(形参列表)
{. . . .
}
创建类模板对象时,必须显式指定具体的数据类型。
#include <iostream>
using namespace std;template<class T1, class T2>
class A
{
public:T1 m_a;T2 m_b;
public:A() { cout << "A()" << endl; }A(T1 a, T2 b) : m_a(a), m_b(b) { cout << "A(int a, int b)" << endl; }T1 geta() { return m_a; } // 在类内声明,类外定义T2 getb();~A() { cout << "~A()" << endl; }
};template<typename T1, typename T2>
T2 A<T1,T2>::getb() { return m_b; }int main()
{// 创建类模板对象时,要显式的指定具体的数据类型A<int, double> obj1(1, 1.2);cout << obj1.geta() << "," << obj1.getb() << endl;// 也可以通过new创建类模板对象A<int, string>* obj2 = new A<int, string>(1, "yoyo");cout << obj2->geta() << "," << obj2->getb() << endl;delete obj2;return 0;
}
类模板的成员函数,只有在实例化类模板后并且在后续使用到该成员函数时,该成员函数才会被实例化(即类模板的成员函数只有在使用时,才会被实例化)。
类模板一旦被实例化,那么这个模板的每个实例都会有自己版本的成员函数(即类模板的成员函数和类模板,有相同的模板参数)。
拷贝构造函数模板、拷贝赋值运算符模板:
- 拷贝构造函数模板/拷贝赋值运算符模板,用于不同类型对象之间的拷贝/赋值。
- 拷贝构造函数/拷贝赋值运算符,用于相同类型对象之间的拷贝/赋值。
#include <iostream>
using namespace std;template<typename T>
class A
{
public: A() {}/* 拷贝构造函数/拷贝赋值运算符,用于相同类型对象之间的拷贝/赋值 */// 拷贝构造函数A(const A<T>& a) {cout << "A(const A<T>& a)" << endl;}// 拷贝赋值运算符A& operator=(const A<T>& a){cout << "A& operator=(const A<T>& a)" << endl;return *this;}/* 拷贝构造函数模板/拷贝赋值运算符模板,用于不同类型对象之间的拷贝/赋值 */// 拷贝构造函数模板template<typename U>A(const A<U>& a) {cout << "A(const A<U>& a)" << endl;}// 拷贝赋值运算符模板template<typename U> A& operator=(const A<U>& a){cout << "A& operator=(const A<U>& a)" << endl;return *this;}
};int main()
{// 相同类型对象之间的拷贝/赋值A<int> a1;A<int> a2(a1);a2 = a1;// 不同类型对象之间的拷贝/赋值A<float> a3;A<int> a4(a3);a4 = a3;return 0;
}
应用:用模板类主,简单实现STL中的容器类
静态数组array:
#include <iostream>
#include <cstring>
using namespace std;// MAX_LEN为非类型模板参数,且可以指定默认值
template<class T, size_t MAX_LEN=10>
class Array
{
private:T items[MAX_LEN];
public:Array() { memset(items, 0, sizeof(items)); }~Array() { }// 两种可以同时存在:// 1、该重载的运算符[],既可以修改成员变量,又可以访问成员变量T& operator[](int idx) { return items[idx]; }// 2、该重载的运算符[],只能访问成员变量const T& operator[](int idx) const { return items[idx]; }
};
int main()
{ // 使用非类型参数的类模板,在实例化模板时,非类型参数必须使用“常量表达式”Array<int, 10> array;array[0] = 1; array[1] = 2;for (int i = 0; i < 10; ++i){cout << array[i] << ",";}cout << endl;return 0;
}
动态数组vector:
#include <iostream>
using namespace std;template<class T>
class vector
{
private:T* items;int len;
public:vector(int size=0) : len(size) { items = new T[len]; }~vector() { delete[] items; items = nullptr; }void resize(int size){if (size <= len) // 只会扩展更大的空间{ return; }T* tmp = new T[size]; // 分配更大的内存空间for (int i = 0; i < len; ++i) // 将原小内存空间中的值赋值给新的更大的内存空间{tmp[i] = items[i];}delete[] items; // 释放原来的内存空间items = tmp; // 指向新的内存空间 len = size;}int size() { return len; }// 两种可以同时存在:// 1、该重载的运算符[],既可以修改成员变量,又可以访问成员变量T& operator[](int idx) { return items[idx]; }// 2、该重载的运算符[],只能访问成员变量const T& operator[](int idx) const { return items[idx]; }
};int main()
{ vector<int> vctor(5);vctor[0] = 1; vctor[1] = 2;for (int i = 0; i < vctor.size(); ++i){cout << vctor[i] << ",";}cout << endl;vctor.resize(10);for (int i = 0; i < vctor.size(); ++i){cout << vctor[i] << ",";}cout << endl;return 0;
}
栈stack:
#include <iostream>
using namespace std;template<class T>
class stack
{
private:int top;int stackSize;T* items;
public:stack(int size=0) : stackSize(size), top(0) { items = new T[size]; }~stack() { delete[] items; items = nullptr; }bool empty() { return (top == 0); }int size() { return stackSize; }int isfull() { return (top == stackSize); }bool push(const T& item){if (!isfull()){ items[top++] = item;return true;}return false;}bool pop(T& item){if (!empty()){item = items[--top];return true;}return false;}
};int main()
{ stack<string> strStack(3);cout << strStack.isfull() << endl;while (!strStack.isfull()){strStack.push("yoyo");} while (!strStack.empty()){string item;strStack.pop(item);cout << item << ",";}cout << endl;return 0;
}
嵌套和递归使用模板类:
描述:容器中有容器,如栈stack中元素是vector动态数组/array静态数组等。
c++11之前,嵌套使用模板类时,> >
之间要有空格,如vector<vector<string> > vvStr
。
各个容器中,必须有自己版本的重载拷贝构造函数、拷贝赋值运算符等,防止在嵌套使用模板类时出现“浅拷贝”,导致内存泄露的问题。
#include <iostream>
using namespace std;template<class T>
class vector
{
private:T* items;int len;
public:vector(int size=0) : len(size) { items = new T[len]; } ~vector() { delete[] items; items = nullptr; }vector& operator=(const vector& m_vector){ delete[] items; items = nullptr;items = new T[m_vector.len]; len = m_vector.len; for (int i = 0; i < len; ++i){items[i] = m_vector.items[i];}return *this;}void resize(int size){if (size <= len) // 只会扩展更大的空间{return;}T* tmp = new T[size]; // 分配更大的内存空间for (int i = 0; i < len; ++i) // 将原小内存空间中的值赋值给新的更大的内存空间{// 如果items中存放的每个元素是一个容器的地址,在该容器中没有重载=运算符的情况下,会发生“浅拷贝”导致内存泄露tmp[i] = items[i]; }delete[] items; // 释放原来的内存空间items = tmp; // 指向新的内存空间 len = size;}int size() { return len; }// 两种可以同时存在:// 1、该重载的运算符[],既可以修改成员变量,又可以访问成员变量T& operator[](int idx){if (idx >= len) // 自动扩展内存空间,为原内存空间+1{resize(len + 1);}return items[idx];}// 2、该重载的运算符[],只能访问成员变量const T& operator[](int idx) const{ return items[idx];}
};template<class T>
class stack
{
private:int top;int stackSize;T* items;
public:stack(int size=3) : stackSize(size), top(0){items = new T[size];}~stack(){delete[] items;items = nullptr;}stack& operator=(const stack& m_stack){delete[] items; items = nullptr;items = new T[m_stack.stackSize]; stackSize = m_stack.stackSize;for (int i = 0; i < stackSize; ++i){// 如果items中存放的每个元素是一个容器的地址,在该容器中没有重载=运算符的情况下,会发生“浅拷贝”导致内存泄露items[i] = m_stack.items[i];}top = m_stack.top;return *this;}bool empty() { return (top == 0); } int size() { return stackSize; } int isfull() { return (top == stackSize); }bool push(const T& item){if (!isfull()){ items[top++] = item;return true;}return false;}bool pop(T& item){if (!empty()){item = items[--top];return true;}return false;}
};int main()
{ /* vector<stack<string>>中,vector动态数组容器嵌套了栈stack容器 */vector<stack<string>> vsStr(2);// 等价于string vsStr[2][3];// 等价于stack<string> vsStr[2];vsStr[0].push("xx"); vsStr[0].push("yy"); vsStr[0].push("zz"); vsStr[1].push("aa"); vsStr[1].push("bb"); vsStr[1].push("cc");for (int i = 0; i < vsStr.size(); ++i){while (!vsStr[i].empty()){string str;vsStr[i].pop(str);cout << str << ",";}cout << endl;}/* stack<vector<string>>中,栈stack容器嵌套了vector动态数组容器 */stack<vector<string>> svStr(2);vector<string> tmp(3); tmp[0] = "aa"; tmp[1] = "bb"; tmp[2] = "cc";svStr.push(tmp); tmp[0] = "ww"; tmp[1] = "xx"; tmp[2] = "yy"; tmp[3] = "zz";svStr.push(tmp); while (!svStr.empty()){ svStr.pop(tmp);for (int i = 0; i < tmp.size(); ++i){ cout << tmp[i] << ",";}cout << endl;} /* vector<vector<string>>中,vector动态数组容器嵌套了vector动态数组容器 */vector<vector<string>> vvStr(2);vector<string> vStr(3);vStr[0] = "aa"; vStr[1] = "bb"; vStr[2] = "cc";vvStr[0] = vStr;vStr[0] = "ww"; vStr[1] = "xx"; vStr[2] = "yy"; vStr[3] = "zz";vvStr[1] = vStr;vStr[0] = "ww"; vStr[1] = "xx"; vStr[2] = "yy"; vStr[3] = "zz";vvStr[2] = vStr; int size1 = vvStr.size();for (int i = 0; i < size1; ++i){ int size2 = vvStr[i].size();for (int j = 0; j < size2; ++j){cout << vvStr[i][j] << ",";}cout << endl;} return 0;
}
类模板特化:
前言:
- 局部特化(偏特化)后,仍是模板;而全特化后则是一个具体的类。
- 必须先有泛化版本,才能有特化版本(特化版本编译器会优先选择,具体化程度越高越优先)
类模板的全特化:
// 常规全特化:必须先有泛化版本,才能有特化版本(特化版本编译器会优先选择)
template<class T, class U>
class TC
{
public:TC() { cout << "<T, U>的泛化版本的默认构造函数" << endl; }void functest() { cout << "<T, U>的泛化版本functest()函数" << endl; }
};// 全特化:所有的类型模板参数,都得用具体的类型代表
template<>
class TC<int, int>
{
public:TC() { cout << "<int, int>的特化版本的默认构造函数" << endl; }void functest() { cout << "<int, int>的特化版本functest()函数" << endl; }
};template<>
class TC<double, int>
{
public:TC() { cout << "<double, int>的特化版本的默认构造函数" << endl; }void functest() { cout << "<double, int>的特化版本functest()函数" << endl; }
};
特化成员函数而不是模板:
void TC<double, double>::functest()
{cout << "<double, double>的特化版本functest()函数" << endl;
}
类模板的偏特化:
根据模板参数的数量,进行局部偏特化:
// 泛化版本
template<class T, class U, class W>
class TC2
{
public:TC2() { cout << "<T, U, W>的泛化版本的默认构造函数" << endl; }void functest() { cout << "<T, U, W>的泛化版本functest()函数" << endl; }
};// 从参数数量上,进行局部特化
template<class U>
class TC2<int, U, int>
{
public:TC2() { cout << "<int, U, int>的偏特化版本的默认构造函数" << endl; }void functest() { cout << "<int, U, int>的偏特化版本functest()函数" << endl; }
};
根据模板参数的范围,进行局部偏特化:
// 泛化版本
template<class T>
class TC3
{
public:TC3() { cout << "<T>的泛化版本的默认构造函数" << endl; }void functest() { cout << "<T>的泛化版本functest()函数" << endl; }
};// 从参数范围上,进行局部偏特化
template<class T>
class TC3 <const T> // const的特化版本
{
public:TC3() { cout << "<const T>的偏特化版本的默认构造函数" << endl; }void functest() { cout << "<const T>的偏特化版本functest()函数" << endl; }
};
template<class T>
class TC3 <T*> // T*的特化版本
{
public:TC3() { cout << "<T*>的偏特化版本的默认构造函数" << endl; }void functest() { cout << "<T*>的偏特化版本functest()函数" << endl; }
};
template<class T>
class TC3 <T&> // T&的特化版本
{
public:TC3() { cout << "<T&>的偏特化版本的默认构造函数" << endl; }void functest() { cout << "<T&>的偏特化版本functest()函数" << endl; }
};
template<class T>
class TC3 <T&&> // T&&的特化版本
{
public:TC3() { cout << "<T&&>的偏特化版本的默认构造函数" << endl; }void functest() { cout << "<T&&>的偏特化版本functest()函数" << endl; }
};
模板类与继承:
模板类继承普通类(常见):
#include <iostream>
using namespace std;class A
{
public:int m_a;A(int a) : m_a(a) { cout << "A()" << endl; } ~A() { cout << "~A()" << endl; }void funcA() { cout << "funcA()=" << m_a << endl; }
};template<class T1, class T2>
class B : public A
{
public:T1 m_b1;T2 m_b2;B(T1 b1, T2 b2, int a) : A(a), m_b1(b1), m_b2(b2) { cout << "B()" << endl; } ~B() { cout << "~B()" << endl; }void funcB() { cout << "funcB()=" << m_b1 << "," << m_b2 << endl; }
};int main()
{// 模板类继承普通类B<int, double> b(1, 1.2, 3);b.funcB();return 0;
}
普通类继承模板类(常见):
情况一:普通类要继承模板类需将自己先变成模板类
#include <iostream>
using namespace std;template<class T1, class T2>
class B
{
public:T1 m_b1;T2 m_b2;B(T1 b1, T2 b2) : m_b1(b1),m_b2(b2) { cout << "B()" << endl; } ~B() { cout << "~B()" << endl; }void funcB() { cout << "funcB()=" << m_b1 << "," << m_b2 << endl; }
};// 普通类要继承模板类需将自己先变成模板类
template<class T1, class T2>
class A : public B<T1,T2>
{
public:int m_a;A(int a, T1 b1, T2 b2) : B<T1,T2>(b1,b2), m_a(a) { cout << "A()" << endl; } ~A() { cout << "~A()" << endl; }void funcA() { cout << "funcA()=" << m_a << endl; }
};int main()
{// 普通类继承模板类,需要指定其基类的模板参数的类型A<int, double> a(3, 1, 1.2);a.funcA();return 0;
}
情况二:普通类继承模板类的实例版本
#include <iostream>
using namespace std;template<class T1, class T2>
class B
{
public:T1 m_b1;T2 m_b2;B(T1 b1, T2 b2) : m_b1(b1),m_b2(b2) { cout << "B()" << endl; } ~B() { cout << "~B()" << endl; }void funcB() { cout << "funcB()=" << m_b1 << "," << m_b2 << endl; }
};class A : public B<int, double>
{
public:int m_a;A(int a, int b1, double b2) : B(b1,b2), m_a(a) { cout << "A()" << endl; } ~A() { cout << "~A()" << endl; }void funcA() { cout << "funcA()=" << m_a << endl; }
}; int main()
{// 普通类继承模板类的实例化版本A a(3, 1, 1.2);a.funcA();return 0;
}
模板类继承模板类:
#include <iostream>
using namespace std;template<class T1, class T2>
class B
{
public:T1 m_b1; T2 m_b2;B(T1 b1, T2 b2) : m_b1(b1),m_b2(b2) { cout << "B()" << endl; } ~B() { cout << "~B()" << endl; }void funcB() { cout << "funcB()=" << m_b1 << "," << m_b2 << endl; }
};template<class T, class T1, class T2>
class A : public B<T1,T2>
{
public:T m_a;A(T a, T1 b1, T2 b2) : B<T1,T2>(b1,b2), m_a(a) { cout << "A()" << endl; } ~A() { cout << "~A()" << endl; }void funcA() { cout << "funcA()=" << m_a << endl; }
}; int main()
{// 模板类继承模板类 A<double, int, double> a(3.2, 1, 1.2);a.funcA();return 0;
}
模板类用于“函数参数和返回值”的三种形式:
普通函数,参数和返回值类型是模板类的实例化版本
#include <iostream>
using namespace std;template<class T1, class T2>
class B
{
public:T1 m_b1; T2 m_b2;B(T1 b1, T2 b2) : m_b1(b1),m_b2(b2) { cout << "B()" << endl; } ~B() { cout << "~B()" << endl; }void funcB() { cout << "funcB()=" << m_b1 << "," << m_b2 << endl; }
};// 普通函数,形参和返回值是模板类B的实例化版本
B<int,double> func(const B<int, double>& a)
{cout << "A<int,double> func(A<int, double>& a)" << endl; return a;
} int main()
{ B<int, double> retB = func(B<int, double>(1,1.2)); return 0;
}
函数模板,参数和返回值是某种的模板类
#include <iostream>
using namespace std;template<class T1, class T2>
class B
{
public:T1 m_b1; T2 m_b2;B(T1 b1, T2 b2) : m_b1(b1),m_b2(b2) { cout << "B()" << endl; } ~B() { cout << "~B()" << endl; }void funcB() { cout << "funcB()=" << m_b1 << "," << m_b2 << endl; }
};// 函数模模板,参数和返回值是某种模板类
template<typename T1, typename T2>
B<T1, T2> func(const B<T1, T2>& a)
{cout << "B<T1, T2> func(const B<T1, T2>& a)" << endl; return a;
} int main()
{ func<int, double>({1,1.2}); return 0;
}
函数模板,参数和返回值是任意类型(支持普通类、模板类和其他类型)
#include <iostream>
using namespace std;template<class T1, class T2>
class B
{
public:T1 m_b1; T2 m_b2;B(T1 b1, T2 b2) : m_b1(b1),m_b2(b2) { cout << "B()" << endl; } ~B() { cout << "~B()" << endl; }void funcB() { cout << "funcB()=" << m_b1 << "," << m_b2 << endl; }
};// 函数模板,参数和返回值是任意类型(普通类、模板类、其他类型)
template<typename T>
T func(T& a) // 规范、常使用此形式!!!
{cout << "T func(T& a)" << endl; return a;
} int main()
{ B<int,double> b(1,1.2);func<B<int, double>>(b);return 0;
}
类模板中的友元:
模板类与友元函数:
*非模板友元:
友元函数不是模板函数,而是利用模板类参数生成的函数,只能在类内实现,本质是全局函数。
#include <iostream>
using namespace std;template<class T1, class T2>
class B
{
public:T1 m_b1;T2 m_b2;B(T1 b1, T2 b2) : m_b1(b1),m_b2(b2) { cout << "B()" << endl; } ~B() { cout << "~B()" << endl; }// 非模板友元:友元函数不是模板函数,而是利用模板类参数生成的函数,只能在类内实现。friend void show(const B<T1, T2>& b) { cout << "friend void show(const B<T1, T2>& b):" << b.m_b1 << "," << b.m_b2 << endl; }
};int main()
{ B<int,double> b(1,1.2);show(b);return 0;
}
约束模板友元:
模板类实例化时,每个实例化的类对应一个友元函数。最好的友元方案!!!
#include <iostream>
using namespace std;// 约束模板友元:模板类实例化时,每个实例化的类对应一个友元函数;
// 需要三个步骤,才能完成声明和定义。
// 第一步:在模板类前面,声明友元函数模板
template<typename T>
void show(T& t); template<class T1, class T2>
class B
{
public:T1 m_b1;T2 m_b2;B(T1 b1, T2 b2) : m_b1(b1),m_b2(b2) { cout << "B()" << endl; } ~B() { cout << "~B()" << endl; }// 第二步:在模板类中,再次声明友元函数模板friend void show<>(B<T1, T2>& b);
};// 第三步:友元函数模板的定义
template<typename T>
void show(T& t)
{ cout << "void show(T& t):" << t.m_b1 << "," << t.m_b2 << endl;
}
template<> // 全特化版本的函数模板,优先于通用函数模板调用,但只适用于这一种参数<int, double>
void show(B<int, double>& b)
{ cout << "void show(B<int, double>& b):" << b.m_b1 << "," << b.m_b2 << endl;
}int main()
{ B<int,double> b1(1,1.2);show(b1);B<int,string> b2(1,"1.2");show(b2);return 0;
}
非约束模板友元:
模板类实例化时,若实例化了n个类也会实例化n个友元函数,且每个实例化的类都拥有n个友元函数。
#include <iostream>
using namespace std; template<class T1, class T2>
class B
{
private:T1 m_b1;T2 m_b2;
public:B() : m_b1(0),m_b2(0) { cout << "B()" << endl; }B(T1 b1, T2 b2) : m_b1(b1),m_b2(b2) { cout << "B(T1 b1, T2 b2)" << endl; }~B() { cout << "~B()" << endl; }template<typename T>friend void show(T& t);
};// 非约束模板友元:模板类实例化时,如果实例化了n个类,也会是实例化n个友元函数,且每个实例化的类都拥有n个友元函数;
template<typename T>
void show(T& t)
{cout << "friend void show(T& t)" << ": " << t.m_b1 << ", " << t.m_b2 << endl;
}int main()
{B<int, double> b(1, 1.2);show(b);return 0;
}
模板类与友元类:
情况一:类模板的某个实例成为友元类
#include <iostream>
using namespace std;template <typename T>
class B;template <typename T>
class A
{// 类模板的某个实例作为友元类friend class B<int>;
public:A() : m_a(T()) { }
private:T m_a;
};template <typename T>
class B
{
public:B() : m_b(static_cast<T>(a.m_a)){A<T> a; cout << m_b << ", " << a.m_a << endl;}
private:T m_b;
};int main()
{B<int> b;return 0;
}
情况二:类模板成为友元类模板
#include <iostream>
using namespace std; template <typename T>
class A
{// 类模板作为友元类模板template <typename U>friend class B;
public:A(T val) : m_a(val) {}
private:T m_a;
};template <typename T>
class B
{
public:B() : m_b(static_cast<T>(a.m_a)){A<double> a(12.5); cout << m_b << ", " << a.m_a << endl;}
private:T m_b;
};int main()
{B<int> b; return 0;
}
情况三:类型模板参数成为友元类
#include <iostream>
using namespace std; // 类模板作为模板参数T,故会被看作该类的友元类
template <typename T>
class A
{ friend T;
public:A(double val) : m_a(val) {}
private:double m_a;
};template <typename T>
class B
{
public:B() : m_b(static_cast<T>(a.m_a)){A<B> a(12.5); // 类模板作为模板参数 cout << m_b << ", " << a.m_a << endl;}
private:T m_b;
};int main()
{B<int> b; return 0;
}
模板模板参数:(将模板类用作模板参数)
// 模板模板参数
template< typename T,// 模板 模板参数template<class> class Container>
class myClass
{
public:myClass(){for (int i = 0; i < 10; i++){myContainer.push_back(static_cast<T>(i));}}
public:// Container代表一个类模板,Container<T>代表一个类Container<T> myContainer;
};template<typename T>
// using定义模板别名
using vector_ = vector<T, allocator<T>>;
template<typename T>
using list_ = list<T, allocator<T>>;void test()
{// vector代表类模板;vector<int>代表类vector<int> myIntVector; // vector_、list_:是模板 模板参数myClass<int, vector_> myIntVectorObj;myClass<int, list_> myIntListObj;
}
可变参数模板(允许模板定义中,含有0~任意个模板参数):
可变参函数模板:
template<typename ...T> // T(可变参类型)代表与多个不同参数相对应的参数类型
void myVTFunc1(T ... args) // args代表一堆类型可以各不同的参数
{cout << sizeof...(args) << endl; // sizeof...():用来计算可变参数量cout << sizeof...(T) << endl;
}template<typename T, typename ...U>
void myVTFunc2(const T& firstArg, const U& ...othersArgs)
{cout << sizeof...(othersArgs) << endl;
}// 参数包的展开:通过同名的递归方式来展开
// 在写代码时,要有一个参数包展开函数和一个同名的递归终止函数
// 1、递归终止函数
void myVTFunc3()
{cout << "参数包展开时执行了,递归终止函数myVTFunc3()" << endl;
}
// 2、这种一个参数、一个包参数的可变参函数模板的写法,最适合参数包展开
template<typename T, typename ...U>
void myVTFunc3(const T& firstArg, const U& ...othersArgs)
{cout << "收到的参数为:" << firstArg << endl;myVTFunc3(othersArgs...); // 递归调用
}void test()
{myVTFunc1();myVTFunc1(10);myVTFunc1(10, 12);myVTFunc1(10, "test", "12", 13.6);cout << ".............." << endl;myVTFunc1(10);myVTFunc2(10, 12);myVTFunc2(10, "test", "12", 13.6);cout << ".............." << endl;myVTFunc3(10, "test", "12", 13.6);
}
若模板函数中,有一个固定参数,其他是可变参数,则如何展开参数包?
一个普通的模板函数嵌套一个参数包展开函数。
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;// 模仿emplace(iterator pos, ...),即通过可变参函数模板给容器中添加元素
vector<int> vctor; // 同名无参的递归终止 和 函数参数展开函数
void m_push_back()
{cout << "\n.....end....." << endl;
}
template<typename T, typename ...Args>
void m_push_back(T arg, Args&... args)
{vctor.push_back(arg);cout << arg << ", ";m_push_back(args...);
}// 普通的模板函数:
template<typename ...Args>
void m_emplace(int num, Args... args)
{cout << num << ":" << endl;m_push_back(args...); // 调用参数包展开函数
} int main()
{m_emplace(10,1,2,3,4,5,6,7,8,9,10); for_each(vctor.begin(), vctor.end(), [](int val) { cout << val << ", "; }); cout << endl;return 0;
}
举例,printf实现:
#include <iostream>
using namespace std;namespace _nmsp
{ void printf(const char* s){while (*s){if (*s == '%' && *(++s) != '%'){throw std::runtime_error("invaild format string: missing arguments");}cout << *(s++);}}template <typename T, typename... Args>void printf(const char* s, T val, Args... args){while (*s){if (*s =='%' && *(++s) != '%'){cout << val;printf(++s, args...);return;}cout << *(s++); // 用来打印不同类型参数间的间隔符}throw std::logic_error("extra arguments provided to printf");}
}int main()
{_nmsp::printf("%s, %d, %f\n", "hello", 12, 15.5);return 0;
}
*可变参类模板:
tuple的“递归继承”/“递归组合”实现,详见下文“STL库”中。
通过“递归继承”的方式,展开参数包:
#include <iostream>
using namespace std;// 通过 "递归继承" 的方式展开参数包
// 1)泛化版本
template<typename ...Args>
class myClassT
{
public:myClassT(){cout << "调用泛化版本的默认构造函数myClassT()" << endl;}
};
// 2)0个模板参数的全特化版本,作为递归结束的标志
template<>
class myClassT<>
{
public:myClassT(){cout << "myClassT<>::myClassT()执行了,this = " << this << endl;}
};
/* 泛化版本存在的原因:0个模板参数的全特化版本的存在,必须先有泛化版本泛化版本的模板参数为可变参数的原因:0个模板参数的全特化版本中,0个模板参数要求泛化版本的模板参数必须是可变参数
*/// 3)偏特化,递归继承
template<typename First, typename ...Others>
class myClassT<First, Others...> : private myClassT<Others...>
{
public:myClassT() : m_val(0){cout << this << endl;}myClassT(First params1, Others... params2) : m_val(params1), myClassT<Others...>(params2...){cout << this << endl;cout << "m_val = " << params1 << endl;}
private:First m_val;
};int main()
{// 调用特化版本(必须先有泛化版本),展开参数包(通过参数包展开和递归结束的特化模板完成)myClassT<int, float, double, char> myCT1(12, 13.3f, 14.4, 'o');cout << "................." << endl;myClassT<int, float, double, const char *> myCT2(12, 13.3f, 14.4, "test");cout << "................." << endl;myClassT<int, float, double, string> myCT3(12, 13.3f, 14.4, "test");cout << "................." << endl;return 0;
}
注意:因采用“递归”的形式,故参数通过“倒序”的形式输出。
通过“递归组合”的方式,展开参数包:
组合关系:
// 组合关系(复合关系):类A和类B之间就是组合关系
class B
{
public://.....
};
class A
{
public://.....
public:B b; // 类A中包含着类B
};
#include <iostream>
using namespace std;// 1)泛化版本
template<typename ...Args>
class myClassT2 {};
// 2)无模板参数的特化版本
template<>
class myClassT2<>
{
public:myClassT2(){cout << "myClassT2<>::myClassT2()执行了,this = " << this << endl;}
};// 3)通过“递归组合”的方式展开参数包
template<typename First, typename ...Others>
class myClassT2<First, Others...>
{
public:myClassT2() : m_val(0){cout << this << endl;}myClassT2(First params1, Others ...params2) : m_val(params1), m_othersVal(params2...){cout << this << endl;cout << "m_val = " << params1 << endl;}
private:// 组合关系:参数多的类包含参数少的类myClassT2<Others...> m_othersVal; First m_val;
};int main()
{myClassT2<int, float, double, char> myCT1(12, 13.3f, 14.4, 'o');cout << "................." << endl;myClassT2<int, float, double, const char*> myCT2(12, 13.3f, 14.4, "test");cout << "................." << endl;myClassT2<int, float, double, string> myCT3(12, 13.3f, 14.4, "test");cout << "................." << endl;return 0;
}
注意:因采用“递归”的形式,故参数通过“倒序”的形式输出。
(多个)基类的参数包的展开:
#include <iostream>
using namespace std;template <typename... parentClassList>
class myClass : public parentClassList...
{
public:myClass() : parentClassList()...{cout << "myClass::myClass() --> " << this << endl; }
};class A1
{
private:char str[10];
public:A1() { cout << "A1::A1() --> " << this << endl; }
};class A2
{
private:char str[20];
public:A2() { cout << "A2::A2() --> " << this << endl; }
};class A3
{
private:char str[10];
public:A3() { cout << "A3::A3() --> " << this << endl; }
};int main()
{myClass<A1, A2, A3> myclass;cout << "size(myClass<A1, A2, A3>) = " << sizeof(myclass) << endl;return 0;
}
可变参数模板的各种特化版本:
#include <iostream>
using namespace std;// 注意:一旦合适的偏特化版本存在,则泛化版本不会执行,但必须存在(先有泛化,才能有特化)!!!
/* ............... 可变参类模版的泛化版本 ............... */
template <typename... Args>
class myClass
{
public:myClass() { printf("myClass的泛化版本,%p,sizeof...(args...) = %d\n", this, sizeof...(Args)); }
};/* ............... 可变参类模版的特化版本 ............... */
/* 一个模板参数、一个可变模板参数 */
template <typename T, typename... Args>
class myClass<T, Args...>
{
public:myClass() { printf("myClass<T, Args...>偏特化版本,%p,sizeof...(args...) = %d\n", this, sizeof...(Args)); }
};/* 两个模板参数; */
template <typename T1, typename T2>
class myClass<T1, T2>
{
public:myClass() { printf("myClass<T1, T2>偏特化版本,%p\n", this); }
};
/* 一个模板参数; */
template <typename T>
class myClass<T>
{
public:myClass() { printf("myClass<T>偏特化版本,%p\n", this); }
}; int main()
{// 一旦存在合适的特化版本,则不会执行泛化版本 myClass<int, float, double, string> mc_params4;myClass<int, float, double> mc_params3;myClass<int, float> mc_params2;myClass<int> mc_params1;// 因没有合适的特化版本,故只能执行泛化版本myClass<> mc_params0; return 0;
}
using用来定义类型(类模板)时,包含了typedef的所有功能:
using用来给“类型模板”起别名:
// str_map_T是类型的别名
template<typename T>
using str_map_T = map<string, T>;
定义类型模板,是一个函数指针模板:
// 定义类型模板,是个函数指针模板
template<typename T>
using myFuncPtrType = int(*)(T, T);
// 不能用typedef等价替换
typedef用来给“类型”起别名:
// using定义模板别名
// typedef:一般用来定义类型别名// 给unsigned int类型,起别名为uint_t;
typedef unsigned int uint_t; // 等价于 using uint_t = unsigned int;
uint_t a;typedef map<string, int> map_s_i; // 等价于 using map_s_i = map<string, int>;
map_s_i myMap;
myMap.insert({ "first", 1 });typedef map<string, string> map_s_s;
map_s_s myMap2;
myMap2.insert({ "first", "1" });
两种不同实现方式的对比:
template<typename T>
class Map_s
{
public:using map_s = map<string, T>; // 等价于typedef map<string, T> map_s;
};// using用来定义类型(类型模板)时,包含了typedef的所有功能;using用来给“类型模板 ” 起别名用
template<typename T>
using str_map_T = map<string, T>; // str_map_T是类型的别名// 希望定义一个键值对中,前边键的类型固定,而值的类型自己指定(通过一个类实现)
Map_s<int>::map_s map_si; // Map_s<int>::map_s代表一个类类型
// 等价于map<string, int> map_si;
map_si.insert({ "first", 1 });str_map_T<int> map_si2;
map_si.insert({ "first", 1 });
定义函数指针类型:
int Add(int i, int j)
{return i + j;
}// 定义函数指针类型
typedef int(*tfPtr)(int, int);
//using tfPtr = int(*)(int, int);
tfPtr tfptr = &Add;
cout << tfptr(12, 13) << endl;// 定义类型模板,是个函数指针模板
template<typename T>
using myFuncPtrType = int(*)(T, T); // 不能用typedef等价替换myFuncPtrType<int> myFPT;
// myFuncPtrType<int>是一个函数指针类型,类型名
// myFPT是一个函数指针变量
myFPT = &Add; // 将函数地址赋给函数指针变量
cout << myFPT(12, 13) << endl;
万能引用、完美转发:
前面详细介绍过了。
模板参数类型推导、查看类型推断结果:
模板参数类型推导:
指针T*或引用类型T&:
- 推荐使用
T&
的模板参数(因引用传值效率高、且模板内部可修改该参数)。 - 若要利用“引用传值效率高”的特点,但不想在模板内部修改,则形参类型可为
const T&
。
#include <iostream>
#include <boost/type_index.hpp>
using namespace std;namespace _test_boost_type_index1
{template <typename T>void func(T& tmpVal){using boost::typeindex::type_id_with_cvr;cout << "............ begin ............" << endl;cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;cout << "tmpVal = " << type_id_with_cvr<decltype(tmpVal)>().pretty_name() << endl;cout << "............ end ............" << endl;}template <typename T>void func_const(const T& tmpVal){using boost::typeindex::type_id_with_cvr;cout << "............ begin ............" << endl;cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;cout << "tmpVal = " << type_id_with_cvr<decltype(tmpVal)>().pretty_name() << endl;cout << "............ end ............" << endl;}void test(){int i = 0;// const属性,会被传递到模板中const int j = 0;// 实参的引用类型,传入模板中,引用会被忽略,即T不会被推导为引用类型const int& k = 0;func(i); // T : int、tmpVal : int&func(j); // T : int const、tmpVal : int const&func(k); // T : int const、tmpVal : int const&func_const(i); // T : int、tmpVal : int const&func_const(j); // T : int、tmpVal : int const&func_const(k); // T : int、tmpVal : int const&}
} namespace _test_boost_type_index2
{template <typename T>void func(T* tmpVal){using boost::typeindex::type_id_with_cvr;cout << "............ begin ............" << endl;cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;cout << "tmpVal = " << type_id_with_cvr<decltype(tmpVal)>().pretty_name() << endl;cout << "............ end ............" << endl;} template <typename T>void func_const(const T* tmpVal){using boost::typeindex::type_id_with_cvr;cout << "............ begin ............" << endl;cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;cout << "tmpVal = " << type_id_with_cvr<decltype(tmpVal)>().pretty_name() << endl;cout << "............ end ............" << endl;}void test(){int i = 0; const int* j = &i; func(&i); // T : int、tmpVal : int*func(j); // T : int const、tmpVal : int const*func_const(&i); // T : int、tmpVal : int const*func_const(j); // T : int、tmpVal : int const*}
}int main()
{_test_boost_type_index1::test();_test_boost_type_index2::test();return 0;
}
万能引用T&&:
T&&
形参类型,则左、右值属性会被保留;const T&&
形参类型,则只接受右值;
#include <iostream>
#include <boost/type_index.hpp>
using namespace std;namespace _test_boost_type_index
{template <typename T>void func(T&& tmpVal){using boost::typeindex::type_id_with_cvr;cout << "............ begin ............" << endl;cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;cout << "tmpVal = " << type_id_with_cvr<decltype(tmpVal)>().pretty_name() << endl;cout << "............ end ............" << endl;} template <typename T>void func_const(const T&& tmpVal){using boost::typeindex::type_id_with_cvr;cout << "............ begin ............" << endl;cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;cout << "tmpVal = " << type_id_with_cvr<decltype(tmpVal)>().pretty_name() << endl;cout << "............ end ............" << endl;}void test(){int i = 10;const int j = 10; func(10); // T : int、tmp : int&& func(i); // T : int&、tmp : int& func(j); // T : int&、tmp : int& func_const(10); // T : int、tmp : int&& //const T&& tmpVal只能接收右值 }
}int main()
{_test_boost_type_index::test();return 0;
}
*传值方式T:
T形参类型,会将实参的引用、const丢弃。
#include <iostream>
#include <boost/type_index.hpp>
using namespace std;namespace _test_boost_type_index
{template <typename T>void func(T tmpVal){using boost::typeindex::type_id_with_cvr;cout << "............ begin ............" << endl;cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;cout << "tmpVal = " << type_id_with_cvr<decltype(tmpVal)>().pretty_name() << endl;cout << "............ end ............" << endl;}template <typename T>void func_const(const T tmpVal){using boost::typeindex::type_id_with_cvr;cout << "............ begin ............" << endl;cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;cout << "tmpVal = " << type_id_with_cvr<decltype(tmpVal)>().pretty_name() << endl;cout << "............ end ............" << endl;}void test(){int i = 0; const int j = 0; const int& k = 0; func(i); // T : int、tmpVal : intfunc(j); // T : int、tmpVal : intfunc(k); // T : int、tmpVal : intfunc_const(i); // T : int、tmpVal : int constfunc_const(j); // T : int、tmpVal : int constfunc_const(k); // T : int、tmpVal : int const}
}int main()
{_test_boost_type_index::test();return 0;
}
T形参类型+传入std::ref()
或std::cref()
(对象包装器),使用:
#include <iostream>
#include <functional>
#include <boost/type_index.hpp>
using namespace std;namespace _test_boost_type_index
{template <typename T>void func(T tmpVal){using boost::typeindex::type_id_with_cvr;cout << "............ begin ............" << endl;cout << "T = " << type_id_with_cvr<T>().pretty_name() << endl;cout << "tmpVal = " << type_id_with_cvr<decltype(tmpVal)>().pretty_name() << endl;cout << "............ end ............" << endl;int& _tmpVal = tmpVal;_tmpVal += 1;}void test(){int i = 10;func(std::ref(i)); // T : class std::reference_wrapper<int>// tmpVal : class std::reference_wrapper<int>cout << i << endl;//func(std::cref(i)); // T : class std::reference_wrapper<int const >// tmpVal : class std::reference_wrapper<int const >}
}int main()
{_test_boost_type_index::test();return 0;
}
*引用折叠(reference-collapsing rules):
折叠规则:如果任意一个为左值引用,则结果为左值引用,否则为右值引用;只有均为右值引用时,折叠后才为右值引用。
template <typename T>
void myFunc(T tmp)// 推断出来的结果:(T、tmp的类型分别为)
T:& --> tmp:& ==引用折叠后==> &
T:& --> tmp:&& ==引用折叠后==> &
T:&& --> tmp:& ==引用折叠后==> &
T:&& --> tmp:&& ==引用折叠后==> &&
函数/类模板的分文件编写:
普通函数/类,声明一般放在.h头文件中,定义放在.cpp源文件中;
函数模板:
-
函数模板,只是函数的描述没有实体(不会分配内存,只会给实例化后的函数分配内存),一般放在.h文件中(不能分文件编写);
-
函数模板全特化版本有实体,编译原理和普通函数一样,声明放在.h头文件中,定义放在.cpp源文件中;
类模板:一般放在.h文件中,且不能分文件编写。
模板总结:
优点:
- 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生;
- 增强了代码灵活性;
缺陷:
- 模板会导致代码膨胀问题,也会导致编译时间变长;
- 出现模板编译错误时,错误信息非常凌乱,不易定位错误;
- 编译器通过一个很复杂的过程,判断是选择泛化版本,还是特化版本;
泛型编程:
面向对象,是一种通过间接层来调用函数,以换取一种抽象。面向对象的多态是一种动态期的多态,即运行时多态。
泛型编程Generic Programming:使用模板template作为主要的编程手段。
泛型编程,则是更直接的抽象,它不会因为间接层而损失效率。
-
泛型编程是一种静态期的多态,通过编译器生成最直接的代码。
template<int n> struct Sum {// Sum(n) = Sum(n - 1) + n;enum Value{N = Sum<n - 1> + n}; }; template<> struct Sum <1> {enum Value{N = 1}; };int main() {// 编译器,就会直接得到1 ~ 100的求和结果cout << Sum<100>::N << endl;return 0; }
-
泛型编程,通过函数模板/类模板,可以将算法和特定类型,结构剥离,尽可能的复用代码。
-
难点:给编译器提供需要生成代码的信息。
c++标准库中,常见的函数模板、类模板、别名模板等功能组件:
std::declval() 函数模板:
只有声明、没有实现,故无法被调用。
功能:
返回某个类型的右值引用(不管该类型是否“有默认构造函数”/“可以创建对象”,均可返回类的右值引用,且在编译期间完成)。
#include <iostream>
#include <boost/type_index.hpp>
using namespace std;class A
{
public:double func() { return 0.0; }
};int main()
{using boost::typeindex::type_id_with_cvr;using _Ty = decltype(std::declval<A>());cout << "_Ty : " << type_id_with_cvr<_Ty>().pretty_name() << endl;// std::decltype(std::declval<A>().func())中,declval会“假想创建出某类对象”,故可(不必经过构造函数)直接调用该类的成员函数cout << "decltype(A:func()) : " << type_id_with_cvr<decltype(std::declval<A>().func())>().pretty_name() << endl;return 0;
}
- 一般用于与
decltype
、sizeof
配合,进行类型推导、占用内存空间计算等; declval
使用时,可在不创建对象的情况下,假想有相应类型的可调用对象,一般搭配decltype使用(不会进行真正计算的);
declval的使用细节:
返回类型本身是不好的;
#include <iostream>
#include <boost/type_index.hpp>
using namespace std;class A
{
public:double func() { return 0.0; }
};template <typename T>
//T& mydeclval() noexcept;
T&& mydeclval() noexcept; // 考虑到declval的语义特性(不能创建对象),故这里返回值只能为左值/右值引用void test()
{using boost::typeindex::type_id_with_cvr;using _Ty = decltype(mydeclval<A>());cout << "_Ty : " << type_id_with_cvr<_Ty>().pretty_name() << endl;cout << "decltype(A:func()) : " << type_id_with_cvr<decltype(mydeclval<A>().func())>().pretty_name() << endl;cout << "sizeof(A) : " << sizeof(mydeclval<A>());
}
返回左值引用,还是返回右值引用;
#include <iostream>
#include <boost/type_index.hpp>
using namespace std;class A {};template <typename T>
//T& mydeclval() noexcept;
T&& mydeclval() noexcept;int test()
{using boost::typeindex::type_id_with_cvr;// 1)mydeclval返回左值引用,则结果为:A&、A&、A&// 2)mydeclval返回右值引用,则结果为:A&&、A&、A&&cout << type_id_with_cvr<decltype(mydeclval<A>())>().pretty_name() << endl;cout << type_id_with_cvr<decltype(mydeclval<A&>())>().pretty_name() << endl;cout << type_id_with_cvr<decltype(mydeclval<A&&>())>().pretty_name() << endl;return 0;
}
引用限定符修饰的成员函数;
#include <iostream>
using namespace std;class A
{
public:void func() { cout << "void func()" << endl; }void funcLVal()& { cout << "void funcLVal()&" << endl; } void funcRVal()&& { cout << "void funcLVal()&&" << endl; }
};int main()
{A a;a.func();a.funcLVal();// declval<A>()返回右值引用,并假装去调用funcRVal()函数decltype(declval<A>().funcRVal())(); decltype(declval<A&&>().funcRVal())(); return 0;
}
推导(可变参数)函数模板返回类型;
#include <iostream>
using namespace std;int func(int val1, int val2)
{return val1 + val2;
}/* declval结合decltype,完成返回类型的推导 */
template <typename Fun, typename... Args>
decltype(std::declval<Fun>()(std::declval<Args>()...)) ft_1(Fun fun, Args... args)
{return fun(args...);
}/* 函数返回类型后置: */
template <typename Fun, typename... Args>
auto ft_2(Fun fun, Args... args) -> decltype(fun(args...))
{return fun(args...);
}int main()
{/* declval结合decltype,完成返回类型的推导 */// std::declval<Fun>()的理解:int(*func_ptr)(int,int) = func;int(* &&func_ptr_)(int, int) = std::move(func_ptr); cout << func_ptr(5, 10) << "\t" << func_ptr_(5, 10) << endl;// std::declval<Args>()...的理解:作为函数的参数类型的推导,本例的推导结果为:int&&、int&& cout << ft_1(func, 5, 10) << endl;/* 函数返回类型后置: */cout << ft_2(func, 5, 10) << endl;return 0;
}
注:也可使用“函数后置返回类型”实现。
std::add_rvalue_reference 类模板:
作用:返回所传入类的右值引用。
int --> int&&
int& --> int& // 会发生引用折叠
int&& --> int&& // 会发生引用折叠
std::true_type、std::false_type 类类型:
using true_type = integral_const<bool, true>;
using false_type = integral_const<bool, false>;// 等价于
template <bool val>
using bool_constant = std::integral<bool, val>;
using true_type = bool_constant<true>;
using false_type = bool_constant<false>;
一般用作基类被继承,这样派生类就具有了“真 或 假”的意味:
// 标准库中
is_pointer、is_union、is_function,均继承了bool_constant(其返回值为true_type 或 false_type)
可作为返回类型使用:
#include <iostream>
#include <type_traits>
using namespace std;template <typename T, bool val>
class A
{
private:template<bool _val>using bool_constant = std::integral_constant<bool, _val>;
public: A(){/* // Error:原因是编译器在编译期间,根据模板参数列表类型将,会将if-else条件分支都进行实例化并编译// ,这会导致T=int时else语句中报错(从const char*到int的转换无效)if (val == true) {a = 15; } else {a = "hello"; }*/AExpress(std::integral_constant<bool, val>()); // 创建了bool_const<val>类的临时对象//等价于:AExpress(bool_constant<val>());cout << _a << endl;}void AExpress(std::true_type) { _a = 15; }void AExpress(std::false_type) { _a = "hello"; }
private:T _a;
};int main()
{A<int, true> a1;A<string, false> a2;return 0;
}
std::void_t别名:
template< class... Args>
using void_t = void;
// 功能:能够检测到SFINAE特性时,出现的非法类型。
判断类中,是否存在某个类型别名:
#include <iostream>
#include <type_traits>
using namespace std;class HasInnerTypeClass
{
public:using type = int;
};class nonInnerTypeClass { };namespace _nmsp1
{// 泛化版本,用于处理无嵌套 ::type 成员的类型:template<typename, typename = std::void_t<>>struct has_type_member : std::false_type { };// 特化版本,可识别拥有嵌套 ::type 成员的类型:template<typename T>struct has_type_member<T, std::void_t<typename T::type>> : std::true_type { };
} namespace _nmsp2
{ // 带参数的宏定义:#define _HAS_TYPE_MEM(paramTy) \template <typename T, typename Arg = std::void_t<>> \struct HTM_##paramTy : std::false_type {}; \template <typename T> \struct HTM_##paramTy<T, std::void_t<typename T::paramTy>> : std::true_type {}; _HAS_TYPE_MEM(type);
}int main()
{ cout << _nmsp1::has_type_member<nonInnerTypeClass>::value << endl;cout << _nmsp1::has_type_member<HasInnerTypeClass>::value << endl; /* 宏定义:编译器会根据paramTy,生成一段与_nmsp1中泛化和特化版本相同的代码 */cout << _nmsp2::HTM_type<nonInnerTypeClass>::value << endl;cout << _nmsp2::HTM_type<HasInnerTypeClass>::value << endl; return 0;
}
判断类中,是否存在某个成员变量:
#include <iostream>
#include <type_traits>
using namespace std;class HasInnerMemberVariableClass
{
public:int m_i;
};class nonInnerMemberVariableClass { };namespace _nmsp1
{// 泛化版本,用于处理无嵌套 ::type 成员的类型:template<typename, typename = std::void_t<>>struct has_variable_member : std::false_type { };// 特化版本,可识别拥有嵌套 ::type 成员的类型:template<typename T>struct has_variable_member<T, std::void_t<decltype(T::m_i)>> : std::true_type { };
} namespace _nmsp2
{ // 带参数的宏定义:#define _HAS_VARIABLE_MEM(paramVar) \template <typename T, typename Arg = std::void_t<>> \struct HVM_##paramVar : std::false_type { }; \template <typename T> \struct HVM_##paramVar<T, std::void_t<decltype(T::paramVar)>> : std::true_type { }; _HAS_VARIABLE_MEM(m_i);
}int main()
{ cout << _nmsp1::has_variable_member<nonInnerMemberVariableClass>::value << endl;cout << _nmsp1::has_variable_member<HasInnerMemberVariableClass>::value << endl; /* 宏定义:编译器会根据paramVar,生成一段与_nmsp1中泛化和特化版本相同的代码 */cout << _nmsp2::HVM_m_i<nonInnerMemberVariableClass>::value << endl;cout << _nmsp2::HVM_m_i<HasInnerMemberVariableClass>::value << endl; return 0;
}
判断类中,是否存在某个成员函数:
#include<iostream>
#include<type_traits>
using namespace std;class HasInnerMemberFuncClass
{
public:void func() { cout << "void HasInnerMemberFuncClass::func()" << endl; }
};class NonInnerMemberFuncClass { };namespace _nmsp1
{// 泛化版本,用于处理无嵌套 ::type 成员的类型:template<typename, typename = std::void_t<>>struct has_func_member : std::false_type { };// 特化版本,可识别拥有嵌套 ::type 成员的类型:template<typename T>struct has_func_member<T, std::void_t<decltype(std::declval<T>().func())>> : std::true_type { };
} namespace _nmsp2
{ // 带参数的宏定义:#define _HAS_VARIABLE_MEM(paramFunc) \template <typename T, typename Arg = std::void_t<>> \struct HFM_##paramFunc : std::false_type { }; \template <typename T> \struct HFM_##paramFunc<T, std::void_t<decltype(std::declval<T>().func())>> : std::true_type { }; _HAS_VARIABLE_MEM(func);
} int main()
{ cout << _nmsp1::has_func_member<NonInnerMemberFuncClass>::value << endl;cout << _nmsp1::has_func_member<HasInnerMemberFuncClass>::value << endl; /* 宏定义:编译器会根据paramVar,生成一段与_nmsp1中泛化和特化版本相同的代码 */cout << _nmsp2::HFM_func<NonInnerMemberFuncClass>::value << endl;cout << _nmsp2::HFM_func<HasInnerMemberFuncClass>::value << endl; return 0;
}
std::is_copy_assignable类模板:
功能:用来判断一个类对象是否可进行拷贝赋值。
#include <iostream>
#include <type_traits>
using namespace std;namespace _nmsp1
{ class A{public:A() { cout << "A::A()" << endl; }A(const A& a) { cout << "A::A(const A& a)" << endl; }/*A& operator=(const A& a) { cout << "A& A::operator=(const A& a)" << endl; return *this; }*/A& operator=(const A& a) = delete;};
}namespace _nmsp2
{template <typename T, typename Arg = std::void_t<>>class IsCopyAssignable : public std::false_type {};template <typename T>// 这里std::declval<A&>() = std::declval<const A&>(),类似于在调用A& operator=(const A& a)拷贝赋值运算符class IsCopyAssignable<T, std::void_t<decltype( std::declval<T&>() = std::declval<const T&>() )>> : public std::false_type {};
}int main()
{ cout << is_copy_assignable<_nmsp1::A>::value << endl;/* 自定义一个is_copy_assignable,并完成类是否存在拷贝赋值运算符cassign */cout << _nmsp2::IsCopyAssignable<_nmsp1::A>::value << endl;return 0;
}
综合举例:重载+运算符,完成两个不同类型vector容器元素求和
实现两个含有不同类型元素的vector容器的对应元素相加,且要求用重载(全局)operator+运算符实现。
注:如果编译器决定要实例化某个模板,则实例化不应该失败(编译错误)。
#include <iostream>
#include <vector>
using namespace std;namespace _nmsp
{ /* 引入一个IsCanAdd的类模板(泛化和特化版本),来判断两个类能相加 */ template <typename T, typename U, typename V = std::void_t<>> // 因泛化版本中未使用T、U参数,故模板参数中的T、U可省略不写,如下:// template <typename, typename, typename V = std::void_t<>> class IsCanAdd : public std::false_type {};template <typename T, typename U> class IsCanAdd<T, U, void_t<decltype(declval<T>() + declval<U>())>> : public std::true_type { };/* --------------------------- */ /* 引入一个VectorAddResult的类模板(泛化和特化版本),来判断两个类相加后的类型 */template <typename T, typename U, bool isCanAdd = IsCanAdd<T, U>::value>class VectorAddResult {public:// std::declval<T>()可在不调用构造函数的情况下//,依然假想有相应类型的可调用对象,一般搭配(不会进行真正计算的)decltype使用using type = decltype(std::declval<T>() + std::declval<U>());};// 不能相加则调用特化版本,即不存在typetemplate <typename T, typename U>class VectorAddResult<T, U, false> { };/* --------------------------- *//* 将 vector<T> 和 vector<U> 中的元素,对应相加 */template <typename T, typename U>using VectorAddResult_t = typename VectorAddResult<T, U>::type;template <typename T, typename U>vector<VectorAddResult_t<T, U>> operator+(const vector<T>& vctor1, const vector<U>& vctor2){vector<VectorAddResult_t<T, U>> tmpVctor;/* ... 完成两个vector容器,内部元素相加的代码 ... */if (vctor1.size() != vctor2.size()){cout << "vctor1和vctor2中的元素个数不同,无法进行直接相加" << endl;return tmpVctor;}tmpVctor.resize(vctor1.size());for (size_t i = 0; i < vctor1.size(); ++i){tmpVctor[i] = vctor1[i] + vctor2[i];}return tmpVctor;}/* --------------------------- */
}
std::conditional类模板:
定义:
/* 表现的是:一种编译器的分支逻辑 */
std::conditional<bool,T,U>::type:
如果非类型模板参数的bool值为true,则type=T
如果非类型模板参数的bool值为false,则type=U
#include <iostream>
#include <type_traits>
using namespace std; namespace _nmsp
{ // 泛化版本:template <bool val, typename T, typename U>class IsConditional{public:using type = T;};// 特化版本:(用来特指val=false的情况)template <typename T, typename U>class IsConditional<false, T, U>{public:using type = U;};
} int main()
{/* std::conditional<bool,T,U>改变第一个传入的非类型模板参数bool的值,即可调整类型(T/U可选) */std::conditional<true, int, float>::type val1 = 10;std::conditional<false, int, float>::type val2 = 10;cout << typeid(val1).name() << endl;cout << typeid(val2).name() << endl;/* 自定义的conditional,即IsConditional */_nmsp::IsConditional<true, int, float>::type val3 = 10;_nmsp::IsConditional<false, int, float>::type val4 = 10;cout << typeid(val3).name() << endl;cout << typeid(val4).name() << endl;return 0;
}
举例:conditional嵌套,实现复杂分支逻辑
/* 要求:1)val > 100,tmp = double2)100 >= val >80, tmp = float3)80 >= val > 40, tmp = int4)val <= 40, tmp = char
*/#include <iostream>
using namespace std; int main()
{constexpr int val = 30;/* std::conditional的嵌套,实现复杂的分支逻辑 */ std::conditional< (val > 100), double, std::conditional< (val > 80), float, std::conditional< (val > 40), int, char >::type >::type >::type tmp; cout << typeid(decltype(tmp)).name() << endl; cout << typeid(tmp).name() << endl; return 0;
}
std::function类模板(“可调用对象包装器”):
使用细节:
详见进阶部分“可调用对象”。
自定义_nmsp::function
类模板:完成可调用对象的包装和调用
#include <iostream>
using namespace std; namespace _nmsp
{/* 用来接收可调用对象,并实现调用: */template <typename T, typename... Args>class CallFunctionObj{public:virtual T invoke(Args... args) const { };};// 创建CallFunctionObj的子类_CallFunctionObjtemplate <typename U, typename T, typename... Args>class _CallFunctionObj : public CallFunctionObj<T, Args...> {public:_CallFunctionObj(U&& funcObj) : functor(std::forward<U>(funcObj)) { }T invoke(Args... args) const {return functor(std::forward<Args>(args)...);};private:U functor; // functor用来接收可调用对象,U是可调用对象类型};/* 封装function类模板(泛化和特化版本): */// 泛化版本:(只是一个声明,因特化存在的前提是,必须有泛化版本)template <typename T>class function;// 特化版本:template <typename T, typename... Args>class function<T(Args...)> {public:// function类中,必须有“构造函数模板”//,用来接收不同的可调用对象(函数对象、lambda表达式、仿函数等)//,且参数应为“万能引用”类型(可接收左值/右值)template <typename U>function(U&& funcObj) // 这里最好是万能引用,避免无法接受某些可调用对象{handler = new _CallFunctionObj<U, T, Args...>(std::forward<U>(funcObj));}~function(){delete handler;}public:// function类中,必须重载()运算符,用作之后定义function类对象后,传参并调用包装的函数对象T operator()(Args... args){return handler->invoke(std::forward<Args>(args)...); // 完成完美转发,即保留参数的左/右值属性}private:CallFunctionObj<T, Args...>* handler;};
}double func(int val1, double val2)
{return (double)val1 + val2;
}class MyFunc
{
public:double operator()(int val1, double val2){return (double)val1 + val2;}
};int main()
{// _nmsp::function包装普通函数对象:_nmsp::function<double(int, double)> funcObj1(func);cout << funcObj1(10, 10.5) << endl;// _nmsp::function包装lambda表达式: _nmsp::function<double(int, double)> funcObj2([](int val1,double val2)->double {return (double)val1 + val2;});cout << funcObj2(10, 10.5) << endl;// _nmsp::function包装仿函数(重载operator()的类):MyFunc myfunc;_nmsp::function<double(int, double)> funcObj3(myfunc);cout << funcObj3(10, 10.5) << endl;return 0;
}
std::remove_all_extents类模板:
功能:将一个数组中的数组类型部分移除,只保留元素类型。
源码分析:remove_all_extents
类模板的实现过程中,用到了“递归模板实例化”的技术手段。
#include <iostream>
#include <type_traits>
using namespace std;namespace _nmsp
{/* remove_all_extents实现源码:*/template <typename T>struct remove_all_extents{using type = T;};template <typename T, size_t N>struct remove_all_extents<T[N]>{using type = typename remove_all_extents<T>::type; // 元编程:多数情况下,都会涉及递归};// 如果首次传入的T为double[10][11][12],则整个递归过程如下:// double[10][11][12] -> double[11][12] -> double[12] -> double// ,其中前三次均调用的是该特化版本,最后一次调用的是泛化版本(没有了非类型模板参数N)// 该特化版本主要用来对“extern int arr[];”这种无边界数组的元素类型萃取template <typename T>struct remove_all_extents<T[]>{using type = typename remove_all_extents<T>::type;};
}int main()
{double arr[10][11][12];cout << typeid(decltype(arr)).name() << endl;cout << typeid(_nmsp::remove_all_extents<decltype(arr)>::type).name() << endl;cout << typeid(std::remove_all_extents<decltype(arr)>::type).name() << endl;return 0;
}
std::integer_sequence类模板:
功能:
用来产生整型序列。
std::make_integer_sequence
为别名模板:
#include <iostream>
#include <utility>
#include <type_traits>
using namespace std;int main()
{// 伪代码: /*template <typename T, size_t N> using make_integer_sequence = std::integral_sequence<T, 0, 1, 2, 3, 4, ..., N-1>;*/std::make_integer_sequence<int, 5> tmpObj;cout << typeid(decltype(tmpObj)).name();// 结果:struct std::integer_sequence<int,0,1,2,3,4>
}
// T为序列中元素的类型,“Args... args”为非类型模板参数
template <typename T, T... args>
class IntegralSequence
{
public:using value_type = T;static constexpr size_t size() noexcept{return sizeof...(args);}
};
自定义“正序”_nmsp::Integer_Sequence类模板:
#include <iostream>
#include <utility>
using namespace std;namespace _nmsp
{/* 定义 IntegerSequence_pushBack 类模板(泛化和特化版本),用来实现“尾插”的功能: */template <typename T, size_t newElement>class IntegerSequence_pushBack; // 因不使用泛化版本,故只声明不定义template <typename T, size_t... Elements, size_t newElement>class IntegerSequence_pushBack<std::integer_sequence<T, Elements...>, newElement>{public:using type = std::integer_sequence<T, Elements..., newElement>;};/* .................................... *//* 自定义 Integer_Sequence 类模板(泛化和特化版本),用来实现make_integer_sequence的功能:*/// 泛化版本:template <typename T, size_t N>class Integer_Sequence{public:// 依次遍历出N-1、...、3、2、1、0,并逐个通过IntegerSequence_pushBack尾插using type = typename IntegerSequence_pushBack<typename Integer_Sequence<T, N - 1>::type, N - 1>::type;};// 特化版本:template <typename T>class Integer_Sequence<T, 1>{public:using type = std::integer_sequence<T, 0>;};/* .................................... *//* 定义别名模板:*/template <typename T, size_t N>using Integer_Sequence_t = typename Integer_Sequence<T, N>::type;/* .................................... */
}int main()
{_nmsp::Integer_Sequence_t<int, 5> tmpObj;cout << typeid(decltype(tmpObj)).name() << endl;return 0;
}
自定义“逆序”_nmsp::Integer_Sequence_Reverse类模板:
#include <iostream>
#include <utility>
using namespace std;namespace _nmsp_reverse
{/* 定义 IntegerSequence_pushBack 类模板(泛化和特化版本),用来实现“尾插”的功能: */template <typename T, size_t newElement>class IntegerSequence_pushFront; // 因不使用泛化版本,故只声明不定义template <typename T, size_t... Elements, size_t newElement>class IntegerSequence_pushFront<std::integer_sequence<T, Elements...>, newElement>{public:using type = std::integer_sequence<T, newElement, Elements...>;};/* .................................... *//* 自定义 Integer_Sequence 类模板(泛化和特化版本),用来实现make_integer_sequence的功能:*/// 泛化版本:template <typename T, size_t N>class Integer_Sequence_Reverse{public:// 依次遍历出N-1、...、3、2、1、0,并逐个通过IntegerSequence_pushBack尾插using type = typename IntegerSequence_pushFront<typename Integer_Sequence_Reverse<T, N - 1>::type, N - 1>::type;};// 特化版本:template <typename T>class Integer_Sequence_Reverse<T, 1>{public:using type = std::integer_sequence<T, 0>;};/* .................................... *//* 定义别名模板:*/template <typename T, size_t N>using Integer_Sequence_Reverse_t = typename Integer_Sequence_Reverse<T, N>::type;/* .................................... */
}
int main()
{_nmsp_reverse::Integer_Sequence_Reverse_t<int, 5> tmpObj;cout << typeid(decltype(tmpObj)).name() << endl;return 0;
}
将一个数字重复多次生成一个类型Repeat_Integer:
通过“递归继承”实现Repeat_Integer尾插多个重复元素。
#include <iostream>
#include <utility>
using namespace std;namespace _nmsp
{/* 定义 IntegerSequence_pushBack 类模板(泛化和特化版本),用来实现“尾插”的功能: */template <size_t Num, size_t RepeatTimes, typename IntegralSequence = std::integer_sequence<size_t>>class RepeatInteger; // 因不使用泛化版本,故只声明不定义// 特化版本1:template <size_t Num, size_t RepeatTimes, size_t... historyIntegralSequence>class RepeatInteger<Num, RepeatTimes, std::integer_sequence<size_t, historyIntegralSequence...>>: public RepeatInteger<Num, RepeatTimes - 1, std::integer_sequence<size_t, historyIntegralSequence..., Num>>{ };// 特化版本2:用来结束递归继承template <size_t Num, size_t... historyIntegralSequence>class RepeatInteger<Num, 0, std::integer_sequence<size_t, historyIntegralSequence...>>{public:using type = std::integer_sequence<size_t, historyIntegralSequence...>;};/* .................................... */
}int main()
{_nmsp::RepeatInteger<3, 5>::type tmpObj1;cout << typeid(decltype(tmpObj1)).name() << endl;_nmsp::RepeatInteger<3, 5, std::integer_sequence<size_t, 1, 2, 3>>::type tmpObj2;cout << typeid(decltype(tmpObj2)).name() << endl;return 0;
}
std::is_union、std::is_class、std::integral_constant类模板:
std::is_union类模板:用来判断一个类是否为联合类型。std::is_union_v别名模板。
std::is_class类模板:用来判断一个类是否为类类型。std::is_class_v别名模板。
std::integral_constant类模板:用于包装,将val包装进一个类中(可用于函数返回值类型等)。
#include <iostream>
#include <type_traits>
using namespace std;class A;namespace _nmsp
{// 变量模板:template<typename T>constexpr bool is_union_v = std::is_union<T>::value;// 变量模板:template<typename T>constexpr bool is_class_v = std::is_class<T>::value;
}int main()
{cout << std::is_union<A>::value << endl;cout << _nmsp::is_union_v<A> << endl;cout << std::is_union_v<A> << endl;cout << is_class<A>::value << endl;cout << _nmsp::is_class_v<A> << endl;cout << std::is_class_v<A> << endl;cout << std::integral_constant<bool, true>::value << endl;cout << std::integral_constant<bool, false>::value << endl;cout << std::integral_constant<int, 5>::value << endl;// integral_constant将“!is_union<A>::value”(编译器即可确定结果)包装成了一个类,可用于函数返回类型cout << std::integral_constant<bool, !is_union<A>::value>::value << endl;return 0;
}
萃取 trait(大量使用在“模板与泛型编程”、“元编程”中):
- 萃取trait技术,用来对模板中的各种模板参数进行管理。
- 利用c++库中提供的接口,可实现细致的萃取,包括“
type_traits
类型萃取”、“iterator_traits
迭代器萃取”等。
fixed traits 固定萃取:
功能:给进来一种类型,萃取出另外一种类型。
常规类型的萃取,举例:
#include <iostream>
using namespace std;namespace _nmsp
{template <typename T>class SumFixedTraits;template <>class SumFixedTraits<char>{public:using sum_Ty = int;};template <>class SumFixedTraits<int>{public:using sum_Ty = long long;};// ...针对其他类型,给出其他不同的特化版本/* 。。。。。。。。。。。。。。。。。。。。。。。。。。。。 */template <typename T, typename U = SumFixedTraits<T>>auto _Sum(const T* begin, const T* end) {typename U::sum_Ty sum{}; // 将sum进行“零初始化”:指针类型初始化为nullptr、数值类型初始化为0、bool类型初始化为false for(; begin != end; ++begin;){sum += *begin; } return sum;}
}int main()
{int arr[3] = {1229992, 12339993, 344449999};cout << _nmsp::_Sum(&arr[0], &arr[2]) << endl;char ch[6] = "hello";cout << _nmsp::_Sum(&ch[0], &ch[2]) << endl;return 0;
}
迭代器萃取:
#include <iostream>
#include <list>
#include <vector>
#include <map>
#include <forward_list>
#include <iterator>
using namespace std;namespace _nmsp
{void _display_category(input_iterator_tag mytag){cout << "input_iterator_tag" << endl;}void _display_category(output_iterator_tag mytag){cout << "output_iterator_tag" << endl;}void _display_category(forward_iterator_tag mytag){cout << "forward_iterator_tag" << endl;}void _display_category(bidirectional_iterator_tag mytag){cout << "bidirectional_iterator_tag" << endl;}void _display_category(random_access_iterator_tag mytag){cout << "random_access_iterator_tag" << endl;}template <typename T>void IteratorTraits(T iter){typename iterator_traits<T>::iterator_category iter_tag; // 萃取机_display_category(iter_tag);if (typeid(typename iterator_traits<T>::iterator_category) == typeid(forward_iterator_tag)){cout << "..........." << "forward_iterator_tag" << "..........." << endl;}if (typeid(typename iterator_traits<T>::iterator_category) == typeid(bidirectional_iterator_tag)){cout << "..........." << "bidirectional_iterator_tag" << "..........." << endl;}if (typeid(typename iterator_traits<T>::iterator_category) == typeid(random_access_iterator_tag)){cout << "..........." << "random_access_iterator_tag" << "..........." << endl;}}
} int main()
{ // 萃取过程:容器类 --> 迭代器类 --> 迭代器的种类_nmsp::IteratorTraits(forward_list<int>::iterator());_nmsp::IteratorTraits(list<int>::iterator());_nmsp::IteratorTraits(vector<int>::iterator());return 0;
}
推导过程详解:“容器类 --> 迭代器类 --> 迭代器的种类”,以list为例:
class _list_iterator
{
public:using iterator_category = bidirectional_iterator_tag;
};class list
{
public:using iterator = _list_iterator;
};list<T>::iterator::iterator_category ==> bidirectional_iterator_tag
通过容器(数组)类型萃取元素类型:
GetElementType类模板(泛化和多个特化版本),来完成该功能:
#include <iostream>
#include <list>
#include <vector>
using namespace std;namespace _nmsp
{// 泛化版本:只声明不使用template <typename T>class GetElementType;// 针对不同的容器/数组,特化出不同的版本template <typename T>class GetElementType<std::vector<T>>{public:using value_type = T;};template <typename T>class GetElementType<std::list<T>>{public:using value_type = T;};template <typename T, size_t _size>class GetElementType<T[_size]>{public:using value_type = T;static const size_t size = _size;};
} int main()
{ cout << typeid(_nmsp::GetElementType<int[10]>::value_type).name() << endl;cout << _nmsp::GetElementType<int[10]>::size << endl;cout << typeid(_nmsp::GetElementType<std::vector<float>>::value_type).name() << endl;cout << typeid(_nmsp::GetElementType<std::list<double>>::value_type).name() << endl;return 0;
}
PrintElementType函数模板,来完成该功能:
#include <iostream>
#include <list>
#include <vector>
using namespace std;namespace _nmsp
{// 泛化版本:只声明不使用template <typename T>class GetElementType;// 针对不同的容器/数组,特化出不同的版本template <typename T>class GetElementType<std::vector<T>>{public:using value_type = T;};template <typename T>class GetElementType<std::list<T>>{public:using value_type = T;};template <typename T, size_t _size>class GetElementType<T[_size]>{public:using value_type = T;static const size_t size = _size;};// 用于函数模板完成T容器/数组类型中,元素类型的打印显示template <typename T>void PrintElementType(const T& t) // 常量左值引用,其左右只值均可绑定,且函数体内部不能修改该对象{cout << typeid(typename GetElementType<T>::value_type).name() << endl;}
} int main()
{ _nmsp::PrintElementType(std::vector<int>());_nmsp::PrintElementType(std::list<double>());float arr[10]; _nmsp::PrintElementType(arr);return 0;
}
优化GetElementType类模板:
#include <iostream>
#include <list>
#include <vector>
using namespace std;namespace _nmsp
{// 泛化版本:只声明不使用template <typename T>class GetElementType{public:using value_type = typename T::value_type;};// 因标准库中,各容器中已定义了value_type可直接获取到元素的类型//,故只需针对“数组”的特化版本 template <typename T, size_t _size>class GetElementType<T[_size]>{public:using value_type = T;static const size_t size = _size;};// 定义别名模板,即变量模板template <typename T>using Element_Ty = typename GetElementType<T>::value_type;// 用于函数模板完成T容器/数组类型中,元素类型的打印显示template <typename T>void PrintElementType(const T& t) // 常量左值引用,其左右只值均可绑定,且函数体内部不能修改该对象{cout << typeid(Element_Ty<T>).name() << endl;}
} int main()
{ _nmsp::PrintElementType(std::vector<int>());_nmsp::PrintElementType(std::list<double>());float arr[10]; _nmsp::PrintElementType(arr);return 0;
}
标准库中,容器已经内部定义了value_type别名,可直接获取元素的类型。
cout << typeid(std::vector<float>::value_type).name() << endl;
引用类型的移除和增加:
引用类型移除remove_reference
类模板或remove_reference_t
变量(别名)模板:
#include <iostream>
using namespace std;namespace _nmsp
{template <typename T, typename U>void Print_is_Same(){cout << "第一个参数的类型:" << typeid(T).name() << endl;cout << "第二个参数的类型:" << typeid(U).name() << endl;cout << "两个参数的类型是否相同:" << std::is_same<T, U>() << endl;// 等价于:std::is_same<T, U>::value} // 别名模板,即变量模板template <typename T>using remove_reference_t = typename std::remove_reference<T>::type;
}int main()
{std::remove_reference<int>::type a;std::remove_reference<int&>::type b;std::remove_reference<int&&>::type c;//等价于上面的写法:(通过别名模板实现)//std::remove_reference_t<int&&> c;//_nmsp::remove_reference_t<int&&> c;_nmsp::Print_is_Same<decltype(a), decltype(b)>();_nmsp::Print_is_Same<decltype(a), decltype(c)>();return 0;
}
自定义实现引用移除的功能:
#include <iostream>
using namespace std;namespace _nmsp
{// 泛化版本:template <typename T>class RemoveReference{public:using type = T;};// 两个特化版本,分别用来去除“左/右值引用”template <typename T>class RemoveReference<T&>{public:using type = T;};template <typename T>class RemoveReference<T&&>{public:using type = T;};// 定义别名模板,即变量模板template <typename T>using RemoveReference_t = typename RemoveReference<T>::type;
}template <typename T, typename U>
void Pring_is_Same()
{cout << "第一个参数的类型:" << typeid(T).name() << endl; cout << "第二个参数的类型:" << typeid(U).name() << endl; cout << "两个参数的类型是否相同:" << std::is_same<T, U>() << endl;
}int main()
{_nmsp::RemoveReference_t<int> a;_nmsp::RemoveReference_t<int&> b;_nmsp::RemoveReference_t<int&&> c;Pring_is_Same<decltype(a), decltype(b)>();Pring_is_Same<decltype(a), decltype(c)>();return 0;
}
引用类型增加add_lvalue_reference
类模板、add_rvalue_reference
类模板:
#include<iostream>
using naemspace std;int main()
{int a;std::add_rvalue_reference<decltype(a)>::type b = 10; std::add_lvalue_reference<decltype(a)>::type c = a; // 这里发生了引用折叠,int&& & --> int&// 也可使用别名模板 /*template<typename T>using add_lvalue_reference_t = typename std::add_lvalue_reference<T>::type;template<typename T>using add_rvalue_reference_t = typename std::add_rvalue_reference<T>::type;*/std::add_rvalue_reference_t<decltype(b)> d = 12; // 这里发生了引用折叠,int&& && --> int&&Pring_is_Same<int, decltype(a)>();Pring_is_Same<int&&, decltype(b)>();Pring_is_Same<int&, decltype(c)>();Pring_is_Same<int&&, decltype(d)>();return 0;
}
is_rvalue_reference
、is_lvalue_reference
,用来判断类型是否为左/右值引用。
自定义实现引用增加的功能:
#include <iostream>
using namespace std;namespace _nmsp
{// 泛化版本:template <typename T>class AddLValueReference{public:using type = T&;};// 两个特化版本,分别用来去除“左/右值引用”template <typename T>class AddRValueReference{public:using type = T&&;};// 定义别名模板,即变量模板template<typename T>using AddLValueReference_t = typename AddLValueReference<T>::type;template<typename T>using AddRValueReference_t = typename AddRValueReference<T>::type;
}template <typename T, typename U>
void Pring_is_Same()
{cout << "第一个参数的类型:" << typeid(T).name() << endl; cout << "第二个参数的类型:" << typeid(U).name() << endl; cout << "两个参数的类型是否相同:" << std::is_same<T, U>() << endl;
}int main()
{int a;_nmsp::AddRValueReference<decltype(a)>::type b = 10; _nmsp::AddLValueReference<decltype(b)>::type c = a; // 这里发生了引用折叠,int&& & --> int&// 也可使用别名模板 _nmsp::AddRValueReference_t<decltype(b)> d = 12; // 这里发生了引用折叠,int&& && --> int&&Pring_is_Same<int, decltype(a)>();Pring_is_Same<int&&, decltype(b)>();Pring_is_Same<int&, decltype(c)>();Pring_is_Same<int&&, decltype(d)>();return 0;
}
std::remove_const类模板和自定义实现const修饰符的去除:
std::remove_const
类模板(泛化和特化版本)。自定义实现const修饰符的去除:
#include <iostream>
using namespace std;namespace _nmsp
{// 泛化版本:template <typename T>class RemoveConst{public:using type = T;};// 特化版本,分别用来去除const修饰符template <typename T>class RemoveConst<const T>{public:using type = T;};// 定义别名模板,即变量模板template<typename T>using RemoveConst_t = typename RemoveConst<T>::type;
}template <typename T, typename U>
void Pring_is_Same()
{cout << "第一个参数的类型:" << typeid(T).name() << endl; cout << "第二个参数的类型:" << typeid(U).name() << endl; cout << "两个参数的类型是否相同:" << std::is_same<T, U>() << endl;
}int main()
{const int a = 10;_nmsp::RemoveConst<decltype(a)>::type b = 10; // 也可使用别名模板 _nmsp::RemoveConst_t<decltype(b)> c = 12; Pring_is_Same<const int, decltype(a)>();Pring_is_Same<int, decltype(b)>();Pring_is_Same<int, decltype(c)>(); return 0;
}
退化decay技术:
const修饰符、&/&&修饰符被丢弃,数组类型 -> 指针类型,函数名 -> 函数指针,均为类型上退化的表现。
自定义实现“const”和“&/&&”修饰符去除:
#include <iostream>
using namespace std;namespace _nmsp
{/* ....................... remove_reference ....................... */// 泛化版本:template <typename T>class RemoveReference{public:using type = T;};// 两个特化版本,分别用来去除“左/右值引用”template <typename T>class RemoveReference<T&>{public:using type = T;};template <typename T>class RemoveReference<T&&>{public:using type = T;};// 定义别名模板,即变量模板template <typename T>using RemoveReference_t = typename RemoveReference<T>::type;/* ....................... remove_const ....................... */// 泛化版本:template <typename T>class RemoveConst{public:using type = T;};// 特化版本,分别用来去除const修饰符template <typename T>class RemoveConst<const T>{public:using type = T;};// 定义别名模板,即变量模板template<typename T>using RemoveConst_t = typename RemoveConst<T>::type; /* ....................... remove_const_reference_写法1 ....................... */template <typename T>class RemoveCR : public RemoveConst<typename RemoveReference<T>::type> {};template <typename T>using RemoveCR_t1 = typename RemoveCR<T>::type;/* ....................... **remove_const_reference_写法2** ....................... */template <typename T>using RemoveCR_t2 = RemoveConst_t<RemoveReference_t<T>>;
}template <typename T, typename U>
void Pring_is_Same()
{cout << "第一个参数的类型:" << typeid(T).name() << endl; cout << "第二个参数的类型:" << typeid(U).name() << endl; cout << "两个参数的类型是否相同:" << std::is_same<T, U>() << endl;
}int main()
{const int& a = 10; _nmsp::RemoveCR_t1<decltype(a)> b1 = 10;_nmsp::RemoveCR_t2<decltype(a)> b2 = 10;Pring_is_Same<int, decltype(b1)>(); Pring_is_Same<int, decltype(b2)>(); return 0;
}
自定义实现“数组类型 --退化为–> 指针类型”:
#include <iostream>
using namespace std;namespace _nmsp_Decay
{// 泛化版本:template <typename T>class Decay : public _nmsp_RemoveCR::RemoveCR<T>{ };// 特化版本1:有边界数组转化为指针template <typename T, size_t size>class Decay<T[size]>{public:using type = T*;}; // 特化版本2:无边界数组转化为指针template <typename T>class Decay<T[]>{public:using type = T*;};
}template <typename T, typename U>
void Pring_is_Same()
{cout << "第一个参数的类型:" << typeid(T).name() << endl; cout << "第二个参数的类型:" << typeid(U).name() << endl; cout << "两个参数的类型是否相同:" << std::is_same<T, U>() << endl;
}int main()
{int arr1[12];Pring_is_Same<int*, typename _nmsp_Decay::Decay<decltype(arr1)>::type>(); // 这里表示arr2整型数组,在其他文件中定义,本文件中只是声明(在本文件和定义了arr2的文件中均可使用)extern int arr2[]; Pring_is_Same<int*, typename _nmsp_Decay::Decay<decltype(arr2)>::type>(); return 0;
}
自定义实现“函数名 --退化为–> 函数指针”:
#include <iostream>
using namespace std;namespace _nmsp_RemoveCR
{/* ....................... remove_reference ....................... */// 泛化版本:template <typename T>class RemoveReference{public:using type = T;};// 两个特化版本,分别用来去除“左/右值引用”template <typename T>class RemoveReference<T&>{public:using type = T;};template <typename T>class RemoveReference<T&&>{public:using type = T;};// 定义别名模板,即变量模板template <typename T>using RemoveReference_t = typename RemoveReference<T>::type;/* ....................... remove_const ....................... */// 泛化版本:template <typename T>class RemoveConst{public:using type = T;};// 特化版本,分别用来去除const修饰符template <typename T>class RemoveConst<const T>{public:using type = T;};// 定义别名模板,即变量模板template<typename T>using RemoveConst_t = typename RemoveConst<T>::type; /* ....................... remove_const_reference_写法1 ....................... */template <typename T>class RemoveCR : public RemoveConst<typename RemoveReference<T>::type> {};template <typename T>using RemoveCR_t1 = typename RemoveCR<T>::type;/* ....................... remove_const_reference_写法2 ....................... */template <typename T>using RemoveCR_t2 = RemoveConst_t<RemoveReference_t<T>>;
}namespace _nmsp_Decay
{// 泛化版本:template <typename T>class Decay : public _nmsp_RemoveCR::RemoveCR<T>{ };// 特化版本1:有边界数组转化为指针template <typename T, size_t size>class Decay<T[size]>{public:using type = T*;}; // 特化版本2:无边界数组转化为指针template <typename T>class Decay<T[]>{public:using type = T*;}; // 特化版本3:将“函数名 --退化为--> 函数指针”template <typename T, typename... Args>class Decay<T(Args...)>{public:using type = T(*)(Args...);};// 定义别名模板,即变量模板template <typename T>using Decay_t = typename Decay<T>::type;
}template <typename T, typename U>
void Pring_is_Same()
{cout << "第一个参数的类型:" << typeid(T).name() << endl; cout << "第二个参数的类型:" << typeid(U).name() << endl; cout << "两个参数的类型是否相同:" << std::is_same<T, U>() << endl;
}double _sum(int val1, double val2)
{return (double)val1 + val2;
}int main()
{Pring_is_Same<double(*)(int,double), typename _nmsp_Decay::Decay<decltype(_sum)>::type>(); Pring_is_Same<double(*)(int,double), typename _nmsp_Decay::Decay_t<decltype(_sum)>>(); return 0;
}
value traits值萃取:
功能:给进来一种类型,萃取出另外一种类型。
value_traits常规范类:
通过 fixed traits
和 value traits
两种方式,实现“各种数组/容器中,不同类型(int、double、char 、类A)元素求和”。
#include <iostream>
using namespace std;class A
{
public:A(int i, int j) : m_i(i), m_j(j) {}A& operator+(const A& a){this->m_i += a.m_i;this->m_j += a.m_j;return *this;}
public:int m_i, m_j;
};namespace _nmsp
{// 泛化版本:template <typename T>class SumFixedTraits_ValueTraits{public:using type = T;};// 特化版本:针对int整型template <>class SumFixedTraits_ValueTraits<int>{public:using type = long long;static constexpr int init_value = 0;};// 特化版本:针对doubletemplate <>class SumFixedTraits_ValueTraits<double>{public:using type = double;static constexpr double init_value = 0.0;};// 特化版本:针对chartemplate <>class SumFixedTraits_ValueTraits<char>{public:using type = char;static constexpr char init_value = '!';};// 特化版本:针对任意类类型A(假定认为类A中只有两个非静态成员变量)template <>class SumFixedTraits_ValueTraits<A>{public:using type = A;// 引入inline变量的解决方法:直接在类A特化的SumFixedTraits_ValueTraits类模板中解决问题,推荐使用!!!//inline static const A init_value = A{0,0}; // -std=c++17 / -std=gnu++17static A init_value;}; A SumFixedTraits_ValueTraits<A>::init_value = A{0,0};// 模板函数Sum,实现[begin,end)之间元素的求和template <typename T, typename U = SumFixedTraits_ValueTraits<T>>auto Sum(const T* begin, const T* end){// 定义和初始化_sum:typename U::type _sum = U::init_value;for (; begin != end; ++begin){_sum = _sum + (*begin); }return _sum;}
} int main()
{int arr1[3] = {1220, 334444, 55555555};double arr2[3] = {1220.8, 6454344.9, 55599955.5};char arr3[3] = "!!";cout << _nmsp::Sum(&arr1[0], &arr1[3]) << endl;cout << _nmsp::Sum(&arr2[0], &arr2[3]) << endl;cout << _nmsp::Sum(&arr3[0], &arr3[3]) << endl;A arr4[3] = { A{1,2},A{3,4},A{5,6} };A a_sum = _nmsp::Sum(&arr4[0], &arr4[3]);cout << a_sum.m_i << "," << a_sum.m_j << endl;return 0;
}
数组/容器中,通过“静态成员函数”,解决元素的初始化:
#include <iostream>
using namespace std;class A
{
public:A(int i, int j) : m_i(i), m_j(j) {}A& operator+(const A& a){this->m_i += a.m_i;this->m_j += a.m_j;return *this;}
public:int m_i, m_j;
};namespace _nmsp
{// 泛化版本:template <typename T>class SumFixedTraits_ValueTraits{public:using type = T;};// 特化版本:针对int整型template <>class SumFixedTraits_ValueTraits<int>{public:using type = long long;static int init_value() { return 0; }};// 特化版本:针对doubletemplate <>class SumFixedTraits_ValueTraits<double>{public:using type = double;static double init_value() { return 0.0; }};// 特化版本:针对chartemplate <>class SumFixedTraits_ValueTraits<char>{public:using type = char;static char init_value() { return (char)'!'; }};// 特化版本:针对任意类类型A(假定认为类A中只有两个非静态成员变量)template <>class SumFixedTraits_ValueTraits<A>{public:using type = A;static A init_value() { return A{0,0}; } }; // 模板函数Sum,实现[begin,end)之间元素的求和template <typename T>auto Sum(const T* begin, const T* end){using Ret_Ty = typename SumFixedTraits_ValueTraits<T>::type;// 通过不同特化版本中,定义“静态成员函数init_value”,并在之后调用该函数,完成(初始化)值萃取Ret_Ty _sum = SumFixedTraits_ValueTraits<T>::init_value();for (; begin != end; ++begin){_sum = _sum + (*begin); }return _sum;}
}
std::is_void类模板,判断传入的是否为void类型:
#include <iostream>
#include <type_traits>
using namespace std;namespace _nmsp
{// 泛化版本:template <typename T>class IsVoid{public:static const bool value = false; };// 特化版本:针对的就是void类型template <>class IsVoid<void>{public:static const bool value = true; };
}int main()
{cout << std::is_void<int>::value << endl;cout << std::is_void<void>::value << endl;cout << _nmsp::IsVoid<int>::value << endl;cout << _nmsp::IsVoid<void>::value << endl; return 0;
}
std::is_same
类模板,判断传入的两个类型是否相等:
#include <iostream>
#include <type_traits>
using namespace std;namespace _nmsp1
{// 泛化版本:template <typename T1, typename T2>class IsSame{public:static const bool value = false; };// 特化版本:针对的就是void类型template <typename T1>class IsSame<T1, T1>{public:static const bool value = true; }; // 变量模板template <typename T1, typename T2>bool IsSame_v = IsSame<T1, T2>::value;
}// 可利用`true_type`和`false_type`完成自定义`IsSame`的简化:
namespace _nmsp2
{// 泛化版本:template <typename T1, typename T2>class IsSame : public std::false_type{ };// 特化版本:针对的就是void类型template <typename T1>class IsSame<T1, T1> : public std::true_type{ }; // 变量模板template <typename T1, typename T2>bool IsSame_v = IsSame<T1, T2>::value;
}int main()
{cout << std::is_same<int, int>::value << endl;cout << std::is_same<int, double>::value << endl;cout << _nmsp1::IsSame<int, int>::value << endl;cout << _nmsp1::IsSame<int, double>::value << endl; cout << _nmsp1::IsSame_v<int, int> << endl;cout << _nmsp1::IsSame_v<int, double> << endl; return 0;
}
使用了SFINAE特性的信息萃取:
用成员函数重载实现is_default_constructible
:
#include <iostream>
#include <type_traits>
using namespace std;namespace _nmsp_1
{template <typename T>class IsDctor{public:// 如果T类型具有默认构造函数default_constructible,则decltype(T())是可推导的//,即会调用该函数模板template <typename = decltype(T())>static std::true_type test(void*);// 具有最低优先级:template <typename = int>static std::false_type test(...); // ...是c语言中,可接受任意0-多个实参public:static constexpr bool value = std::is_same<decltype(test(nullptr)), std::true_type>::value;};
}namespace _nmsp_2
{template <typename T>class IsDctorHelper{public:// 如果T类型具有默认构造函数default_constructible,则decltype(T())是可推导的//,即会调用该函数模板template <typename = decltype(T())>static std::true_type test(void*);// 具有最低优先级:template <typename = int>static std::false_type test(...); // ...是c语言中,可接受任意0-多个实参public:using type = decltype(test(nullptr));}; template <typename T>class IsDctor: public IsDctorHelper<T>::type{ };
}class A
{
public:A() = delete;
};
class B
{
public:B() {}
}; int main()
{cout << std::is_default_constructible<int>::value << endl;cout << std::is_default_constructible<A>::value << endl;cout << std::is_default_constructible<B>::value << endl;cout << _nmsp_1::IsDctor<int>::value << endl;cout << _nmsp_1::IsDctor<A>::value << endl;cout << _nmsp_1::IsDctor<B>::value << endl;cout << _nmsp_2::IsDctor<int>::value << endl;cout << _nmsp_2::IsDctor<A>::value << endl;cout << _nmsp_2::IsDctor<B>::value << endl;return 0;
}
用类模板特化实现is_default_constructible
:
#include <iostream>
#include <type_traits>
using namespace std;namespace _nmsp
{// 类模板的泛化版本template <typename T, typename U = std::void_t<>> class IsDctor : public std::false_type{ }; // 类模板的特化版本template <typename T> class IsDctor<T, std::void_t<decltype(T())>> : public std::true_type{ };
} class A
{
public:A();
};
class B
{
public:B() = delete;
}; int main()
{ cout << std::is_default_constructible<A>::value << endl; cout << std::is_default_constructible<B>::value << endl; cout << _nmsp::IsDctor<A>::value << endl; cout << _nmsp::IsDctor<B>::value << endl; return 0;
}
用成员函数重载实现is_convertible
:
判断两个类型是否可以相互转换。
#include <iostream>
#include <type_traits>
using namespace std;namespace _nmsp
{template <typename From, typename To>class IsConvertibleHelper{public: static void testFunc(To);// 如果T类型具有默认构造函数default_constructible,则decltype(T())是可推导的//,即会调用该函数模板template <typename = decltype(testFunc(std::declval<From>()))>static std::true_type test(void*);// 具有最低优先级: static std::false_type test(...); // ...是c语言中,可接受任意0-多个实参public:using type = decltype(test(nullptr)); // 此时,type = std::true_type / std::false_type}; template <typename From, typename To>class IsConvertible: public IsConvertibleHelper<From, To>::type{ };// 定义变量模板 template <typename From, typename To>static constexpr bool IsConvertible_v = IsConvertible<From, To>::value;
} class A
{ };class B : public A
{ };int main()
{cout << std::is_convertible<int, double>::value << endl;cout << std::is_convertible<A, B>::value << endl;cout << std::is_convertible<B, A>::value << endl;cout << _nmsp::IsConvertible<int, double>::value << endl; cout << _nmsp::IsConvertible<A, B>::value << endl; cout << _nmsp::IsConvertible<B, A>::value << endl; cout << _nmsp::IsConvertible_v<int, double> << endl;cout << _nmsp::IsConvertible_v<A, B> << endl;cout << _nmsp::IsConvertible_v<B, A> << endl;return 0;
}
用成员函数重载实现is_class
:
判断一个类型是否为一个类。
#include <iostream>
#include <type_traits>
using namespace std;namespace _nmsp
{template <typename T>class IsClass {public: // 如果U是一个联合体,则test()的返回类型为std::iteger_constant<bool, true>template <typename U>static std::integral_constant<bool, !std::is_union<U>::value> test(int U::*);// 这里的形参类型为一个成员变量指针,可接受nullptr和地址/*// 类A的成员变量指针:指向类A中int成员变量int A::*memVar_ptr = nullptr;*/// 具有最低优先级: template <typename>static std::integral_constant<bool, false> test(...); // ...是c语言中,可接受任意0-多个实参public:static constexpr bool value = std::is_same<decltype(test<T>(nullptr)), std::true_type>::value; }; // 定义变量模板 template <typename T>static constexpr bool IsClass_v = IsClass<T>::value;
} class A
{ }; int main()
{cout << std::is_class<int>::value << endl;cout << std::is_class<string>::value << endl;cout << std::is_class<A>::value << endl;cout << _nmsp::IsClass<int>::value << endl; cout << _nmsp::IsClass<string>::value << endl; cout << _nmsp::IsClass<A>::value << endl; cout << _nmsp::IsClass_v<int> << endl;cout << _nmsp::IsClass_v<string> << endl;cout << _nmsp::IsClass_v<A> << endl;return 0;
}
用成员函数重载实现is_base_of
:
判断是否是一个类类型(非联合体类型)的类模板,即两个类是否有父子关系。
#include <iostream>
#include <type_traits>
using namespace std;namespace _nmsp
{template <typename Base, typename Derived> // <父, 子>class IsBaseOf {public: template <typename T>static std::true_type test(T*); // 具有最低优先级: template <typename>static std::false_type test(void*); // void*可接受任意指针类型变量// 返回值类型后置:template <typename _Base, typename _Derived>static auto test_middle() -> decltype(test<_Base>(static_cast<_Derived*>(nullptr))); public:static constexpr bool value = std::is_same<std::integral_constant<bool, std::is_class<Base>::value && std::is_class<Derived>::value && decltype(test_middle<Base, Derived>())::value>, std::true_type>::value; }; // 定义变量模板 template <typename Base, typename Derived>static constexpr bool IsBaseOf_v = IsBaseOf<Base, Derived>::value;
} class A
{ };
class B : public A
{ }; int main()
{ cout << std::is_base_of<A, B>::value << endl; cout << _nmsp::IsBaseOf<A, B>::value << endl; cout << _nmsp::IsBaseOf_v<A, B> << endl; return 0;
}
迭代器萃取:
举例:advance()函数,如何判断迭代器的类型?
/* advance函数模板: */
template <typename Iter>函数1
void advanceI(Iter iter, int n) // iter前进n个单位
{//1. 数组下标,i+=n;//2. 链表while(n--) { iter=iter->next; }
}template<typename _InputIterator, typename _Distance>
inline _GLIBCXX17_CONSTEXPR void advance(_InputIterator& __i, _Distance __n)
{// concept requirements -- taken care of in __advancetypename iterator_traits<_InputIterator>::difference_type __d = __n;std::__advance(__i, __d, std::__iterator_category(__i));
}
/* advance针对不同的迭代器的函数模板的重载版本: */
/* 注:每个重载的函数模板,均在形参列表末尾有一个iterator_tag */
template<typename _InputIterator, typename _Distance>
inline _GLIBCXX14_CONSTEXPR void __advance(_InputIterator& __i, _Distance __n, input_iterator_tag)
{while (__n--) { ++__i; }
}
template<typename _BidirectionalIterator, typename _Distance>
inline _GLIBCXX14_CONSTEXPR void __advance(_BidirectionalIterator& __i, _Distance __n, bidirectional_iterator_tag)
{if (__n > 0) {while (__n--) { ++__i; }} else {while (__n++) { --__i; }}
}
template<typename _RandomAccessIterator, typename _Distance>
inline _GLIBCXX14_CONSTEXPR void __advance(_RandomAccessIterator& __i, _Distance __n, random_access_iterator_tag)
{if (__builtin_constant_p(__n) && __n == 1) {++__i;} else if (__builtin_constant_p(__n) && __n == -1) {--__i;} else {__i += __n;}
}
//STL在<stl_iterator_base_types.h>中定义了五种迭代器类型,详见上图。// 迭代器类(只给出了typedef):
template<typename _Category, typename _Tp, typename _Distance = ptrdiff_t, typename _Pointer = _Tp*, typename _Reference = _Tp&>
struct iterator
{// One of the @link iterator_tags tag types@endlink.typedef _Category iterator_category;// The type "pointed to" by the iterator.typedef _Tp value_type;// Distance between iterators is represented as this type.typedef _Distance difference_type;// This type represents a pointer-to-value_type.typedef _Pointer pointer;// This type represents a reference-to-value_type.typedef _Reference reference;
};
// 迭代器类类型:
template<typename _Iterator>
struct iterator_traits
{typedef typename _Iterator::iterator_category iterator_category;typedef typename _Iterator::value_type value_type;typedef typename _Iterator::difference_type difference_type;typedef typename _Iterator::pointer pointer;typedef typename _Iterator::reference reference;
};
// 普通指针类型:
template<typename _Tp>
struct iterator_traits<_Tp*>
{typedef random_access_iterator_tag iterator_category;typedef _Tp value_type;typedef ptrdiff_t difference_type;typedef _Tp* pointer;typedef _Tp& reference;
};
策略 policy 技术中的算法策略:
常规范例:
普通策略类:
#include <iostream>
#include <climits>
#include <type_traits>
using namespace std;namespace _nmsp_MinValPolicyClass
{// 泛化版本:template <typename T>class MinFixedTraits;// 特化版本:template <>class MinFixedTraits<int>{public:using type = int;static constexpr int initVal = INT_MAX;};// 普通策略类class MinValPolicy{public:template <typename T, typename U>static void algorithm(T& minVal, U val){if (minVal > val){minVal = val;}}};template <typename T, typename U = MinFixedTraits<T>, typename V = MinValPolicy>auto implementFunc(const T* begin, const T* end) {typename U::type result = U::initVal;for (; begin != end; ++begin){V::algorithm(result, *begin);}return result;}
}namespace _nmsp_SumPolicyClass
{// 泛化版本:template <typename T>class SumFixedTraits;// 特化版本:template <>class SumFixedTraits<int>{public:using type = int;static constexpr int initVal = 0;};// 普通策略类class SumPolicy{public:template <typename T, typename U>static void algorithm(T& sum, U val){sum += val;}};template <typename T, typename U = SumFixedTraits<T>, typename V = SumPolicy>auto implementFunc(const T* begin, const T* end){typename U::type result = U::initVal;for (; begin != end; ++begin){V::algorithm(result, *begin);}return result;}
}int main()
{ int arr[8] = { 4,3,2,6,7,11,13,0 };cout << _nmsp_MinValPolicyClass::implementFunc<int>(&arr[0], &arr[8]) << endl;cout << _nmsp_SumPolicyClass::implementFunc<int>(&arr[0], &arr[8]) << endl;return 0;
}
策略类模板:
#include <iostream>
#include <climits>
#include <type_traits>
using namespace std;namespace _nmsp_MinValPolicyClass
{// 泛化版本:template <typename T>class MinFixedTraits;// 特化版本:template <>class MinFixedTraits<int>{public:using type = int;static constexpr int initVal = INT_MAX;};// 策略类模板template <typename T, typename U>class MinValPolicy{public: static void algorithm(T& minVal, U val){if (minVal > val){minVal = val;}}};template <typename T, typename U = MinFixedTraits<T>, typename V = MinValPolicy<typename U::type, T>>auto implementFunc(const T* begin, const T* end) {typename U::type result = U::initVal;for (; begin != end; ++begin){V::algorithm(result, *begin);}return result;}
}namespace _nmsp_SumPolicyClass
{// 泛化版本:template <typename T>class SumFixedTraits;// 特化版本:template <>class SumFixedTraits<int>{public:using type = int;static constexpr int initVal = 0;};// 策略类模板template <typename T, typename U>class SumPolicy{public: static void algorithm(T& sum, U val){sum += val;}};template <typename T, typename U = SumFixedTraits<T>, typename V = SumPolicy<typename U::type, T>>auto implementFunc(const T* begin, const T* end){typename U::type result = U::initVal;for (; begin != end; ++begin){V::algorithm(result, *begin);}return result;}
}int main()
{ int arr[8] = { 4,3,2,6,7,11,13,0 };cout << _nmsp_MinValPolicyClass::implementFunc<int>(&arr[0], &arr[8]) << endl;cout << _nmsp_SumPolicyClass::implementFunc<int>(&arr[0], &arr[8]) << endl;return 0;
}
萃取 trait 技术与策略 policy 技术的比较:
萃取 trait:给进去一个类型,输出一个类型或者值(注重:类型 / 值)。
- 通常只需在模板类中指定 类型(使用using关键字) / 常量值(使用static constexpr 类型 变量名 = 值,表示为常量表达式)。
- 通常需要类模板来实现,且类模板包含“泛化和特化版本”。
策略 policy:给进去一个类型,萃取出一个算法或不同的功能实现(注重:动作 / 行为)。
- 通常需要在模板类中指定成员函数。
- 通常使用“普通类或模板类”,就可实现。