类模板是生产类的模具,通过给定的模板参数生成具体的类,也就是实例化一个特定的类,引入类模板的目的是减少代码冗余.类模板中只只有被调用的时候才会实例化该函数。
#include
#include
#include
template <typename T> // T是类型模板参数
class MyVector { // MyVector可以称为类名或者类模板,MyVector可以称为类型名
public:
using myiterator = T*;
public:
MyVector();
MyVector& operator=(const MyVector&); // 赋值运算符重载
void myfunc()
{
std::cout << "called myfunc" << std::endl;
}
static void myfuncs()
{
std::cout << "called myfuns" << std::endl;
}
myiterator mybegin(); // 迭代器的起始位置
myiterator myend(); // 迭代器的结束位置
};
// 类模板实现
template <typename T>
MyVector<T>::MyVector() // 类外构造函数的实现
{
std::cout << "MyVector Constructed" << std::endl;
}
int main(int argc, char** argv)
{
MyVector<int> vec;
vec.myfunc();
MyVector<std::string>::myfuncs();
return 0;
}
MyVector(T val)
{
std::cout << "type t:" << typeid(val).name() << std::endl;
}
MyVector vec(10);// 无需指定模板参数
vec.myfunc();
针对类模板的每个构造函数都有一个隐式的模板参数推断机制存在,称为隐式推断指南
// template
// A(T, T) -> A
// 表达式出现->左侧部分内容或者形式时,请推断成->右侧的类型。右侧类型也被称为“指南类型”
//->左侧部分:该推断指南所对应的构造函数的函数声明,多个参数之间用,分割
//->右侧部分:类模板名,接着一个尖括号,尖括号中时模板参数名
//整个推断指南的含义:当调用带2个参数的构造函数通过类模板A创建相关对象时,请用所提供的构造函数的实参来推断类模板A的模板参数类型
#include
#include
#include
template <typename T>
class A {
public:
A(T val1)
{
std::cout << "A(T val1) construct called"
<< " type t:" << typeid(val1).name() << std::endl;
}
A(T val1, T val2)
{
std::cout << "A(T val1, T val2) construct called"
<< " type t:" << typeid(val1).name() << std::endl;
}
};
int main(int argc, char** argv)
{
A obj1(12, 15); // A
A obj2(12.2); // A
return 0;
}
一般来讲所写的类模板都是泛化的类模板。而特化的类模板是通过泛化的类模板生成的,因此先要有泛化版本,才有特化版本
全特化就是把A泛化版本中的所有模板参数都用具体的类型来代替构成的一个特殊的版本(全特化版本)
#include
#include
#include
template <typename T, typename U>
class A {
public:
A()
{
std::cout << "A 泛化版本构造函数执行了" << std::endl;
}
void func()
{
std::cout << "A 泛化版本func() 执行了" << std::endl;
}
};
// 全特化
template <> // 所有类型模板参数都由具体的类型代表,所以<>里空了
class A<int, int> {
public:
A()
{
std::cout << "A 全特化版本构造函数执行了" << std::endl;
}
// 类中实现
void func()
{
std::cout << "A 全特化版本func()执行了" << std::endl;
}
void func2()
};
// 类外实现
void A<int, int>::func2()
{
std::cout << "A 全特化版本func2()执行了" << std::endl;
}
int main(int argc, char** argv)
{
// 泛化
A<int, float> obj1;
obj1.func();
// 全特化
A<int, int> obj2;
obj2.func();
obj2.func2();
return 0;
}
#include
#include
#include
template <typename T, typename U>
class A {
public:
A()
{
std::cout << "A 泛化版本构造函数执行了" << std::endl;
}
void func()
{
std::cout << "A 泛化版本func() 执行了" << std::endl;
}
};
// 类A成员函数void func()的全特化
template <>
void A<int, float>::func()
{
std::cout << "A普通成员函数void func()的全特化版本 A::func() 执行了" << std::endl;
}
int main(int argc, char** argv)
{
A<int, float> obj1;
obj1.func();
return 0;
}
#include
#include
#include
template <typename T, typename U>
class A {
public:
A()
{
std::cout << "A 泛化版本构造函数执行了" << std::endl;
}
void func()
{
std::cout << "A 泛化版本func() 执行了" << std::endl;
}
static int m_val; // 声明一个静态成员变量
};
// 静态成员变量定义
template <typename T, typename U>
int A<T, U>::m_val = 50;
// 静态成员变量的全特化
template <>
int A<double, int>::m_val = 100;
int main(int argc, char** argv)
{
A<int, double> obj1;
std::cout << "m_val:" << obj1.m_val << std::endl;
A<double, int> obj2;
std::cout << "m_val:" << obj2.m_val << std::endl;
return 0;
}
注意:如果进行了普通成员函数的全特化或者是静态成员变量的全特化,那么就无法使用这些全特化指定类型来对整个类模板进行全特化了。下例失败
// ERROR 类模板全特化
template<>
class A<double, int>
{
A()
{
std::cout << "A 全特化版本构造函数执行了" << std::endl;
}
}
#include
#include
#include
template <typename T, typename U>
class A {
public:
A()
{
std::cout << "A 泛化版本构造函数执行了" << std::endl;
}
void func()
{
std::cout << "A 泛化版本func() 执行了" << std::endl;
}
};
// 偏特化
template <typename U>
class A<double, U> {
public:
A()
{
std::cout << "A 偏特化版本构造函数执行了" << std::endl;
}
void func();
};
template <typename U>
void A<double, U>::func()
{
std::cout << "A 偏特化版本func() 执行了" << std::endl;
}
int main(int argc, char** argv)
{
// 泛化
A<int, double> obj1;
obj1.func();
// 偏特化
A<double, int> obj2;
obj2.func();
return 0;
}
int -> const int, T -> T*, T -> T&, T -> T&&
#include
#include
#include
template <typename T, typename U>
class A {
public:
A()
{
std::cout << "A 泛化版本构造函数执行了" << std::endl;
}
void func()
{
std::cout << "A 泛化版本func() 执行了" << std::endl;
}
};
// 范围偏特化
template <typename T, typename U>
class A<const T, U*> {
public:
A()
{
std::cout << "A 偏特化版本构造函数执行了" << std::endl;
}
void func();
};
template <typename T, typename U>
void A<const T, U*>::func()
{
std::cout << "A 偏特化版本func() 执行了" << std::endl;
}
int main(int argc, char** argv)
{
// 泛化
A<int, double> obj1;
obj1.func();
// 偏特化
A<const double, int*> obj2;
obj2.func();
return 0;
}
类型模板参数缺省值的规则:如果某个模板参数有缺省值,那么从这个缺省值的模板参数开始, 后面的所有模板参数都得有缺省值。类模板偏特化版本中的类型模板参数不能有缺省值
#include
#include
#include
// template
template <typename T = int, typename U = int> // 指定缺省类型
class A {
public:
A()
{
std::cout << "A 泛化版本构造函数执行了" << std::endl;
}
void func()
{
std::cout << "A 泛化版本func() 执行了" << std::endl;
}
};
int main(int argc, char** argv)
{
A obj1;
obj1.func();
A<> obj2; // 使用了缺省模板参数,所以<>中啥也不提供
A<double> obj3; //<>中第一个类型参数不使用缺省参数,第二个类型使用缺省参数
return 0;
}
#include
#include
#include
template <typename T, typename U = T*> // 后面的模板参数U依赖前面的模板参数T
class A {
public:
A()
{
std::cout << "A 泛化版本构造函数执行了" << std::endl;
}
void func()
{
std::cout << "A 泛化版本func() 执行了" << std::endl;
}
};
int main(int argc, char** argv)
{
A<double> obj1;
obj1.func();
return 0;
}
#include
#include
#include
// 声明1 指定V,W的缺省参数
template <typename T, typename U, typename V = int, typename W = char>
class A;
// 声明2 指定U缺省值,相当于U、V、W都指定了缺省参数
template <typename T, typename U = char, typename V, typename W>
class A;
// 定义泛化版本
template <typename T, typename U, typename V, typename W>
class A {
public:
A()
{
std::cout << "A 泛化版本构造函数执行了" << std::endl;
}
void func()
{
std::cout << "A 泛化版本func() 执行了" << std::endl;
}
};
int main(int argc, char** argv)
{
A<double> obj1; // 第2、3、4个模板参数采用缺省值,所以<>里只提供了一个类型模板实参
obj1.func();
return 0;
}
#include
#include
// ArraySize
template <typename T, typename U, size_t ArraySize = 8>
class A {
public:
A()
{
std::cout << "A 泛化版本构造函数执行了,array_size:" << ArraySize << std::endl;
}
void func();
private:
T m_arr[ArraySize]; // ArraySize在编译器编译期间确定
};
// 非类型模板参数,类的成员函数类外实现
template <typename T, typename U, size_t ArraySize>
void A<T, U, ArraySize>::func()
{
std::cout << "A 泛化版本A::func执行了" << std::endl;
}
int main(int argc, char** argv)
{
A<double, int> obj1; // 缺省的第三个模板参数是8
obj1.func();
A<double, int, 10> obj2;
obj2.func();
return 0;
}
#include
#include
template <typename T1>
class A {
public:
A()
{
std::cout << "A 泛化版本构造函数执行了" << std::endl;
}
template <typename T2>
A(T2 v1, T2 v2); // 构造函数模板,引入了自己的模板参数T2
A(double v1, double v2) // 构造函数
{
std::cout << " A(double, double)构造函数执行了" << std::endl;
}
A(T1 v1, T1 v2)
{
std::cout << " A(T1, T1)构造函数执行了" << std::endl;
}
template <typename T3>
void func(T3 val) // 普通成员函数模板
{
std::cout << "A func va;:" << val << std::endl;
}
private:
T1 m_val;
static constexpr int m_static_val = 200;
};
// 类外实现类模板的构造函数模板
template <typename T1> // 类模板
template <typename T2> // 成员函数模板
A<T1>::A(T2 val1, T2 val2)
{
std::cout << "A::A(T2, T2) 构造函数执行了, type T2:" << typeid(val2).name()
<< " type m_val:" << typeid(m_val).name() << std::endl;
}
int main(int argc, char** argv)
{
A<float> obj1(1, 2); // 实例化了A这样一个类型,并用int类型来实例化构造函数
A<float> obj2(1.2, 2.3); // A上面已经实例化了,并用double类型来实例化构造函数
A<float> obj3(1.2f, 2.3f); // 用float类型来实例化构造函数
return 0;
}
拷贝构造函数模板不是拷贝构造函数,拷贝赋值运算符模板不是拷贝赋值运算符,构造函数模板也不是构造函数。拷贝构造函数或者拷贝赋值运算符要求拷贝的对象类型完全相同,而拷贝构造函数模板和拷贝赋值运算符模板就没有这种要求。拷贝构造函数模板永远不会成为拷贝构造函数。当**类型不同但 都是类模板实例化出来的两个对象时(如A和A)用一个拷贝构造另一个时,拷贝构造函数模板才会调用
#include
#include
template <typename T1>
class A {
public:
A()
{
std::cout << "A 泛化版本构造函数执行了" << std::endl;
}
// 拷贝构造函数模板
template <typename U>
A(const A<U>& other)
{
std::cout << "A::A(const A& other)构造函数执行了" << std::endl;
}
// 拷贝赋值运算符模板
template <typename U>
A<T1>& operator=(const A<U>& other)
{
std::cout << "A &operator=(const A &other)构造函数执行了" << std::endl;
return *this;
}
template <typename T3>
void func(T3 val) // 普通成员函数模板
{
std::cout << "A func va;:" << val << std::endl;
}
private:
T1 m_val;
};
int main(int argc, char** argv)
{
A<double> obj1;
A<double> obj2(obj1); // 未执行拷贝构造函数模板的代码,因为obj1、obj2类型相同,
// 本该执行拷贝构造函数,但是因为类模板中没有拷贝构造函数,所以编译器内部实际是执行了按值拷贝的一个操作
// 拷贝构造函数模板永远不会称为拷贝构造函数,编译器不会调用拷贝构造函数模板来代替拷贝构造函数
A<int> obj3(obj1); // obj1:A obj3:A,两者类型不同
A<int> obj4 = obj1;
return 0;
}
#include
#include
template <typename T1>
class A {
public:
A()
{
std::cout << "A 泛化版本构造函数执行了" << std::endl;
}
// 拷贝构造函数模板
template <typename U>
A(const A<U>& other)
{
std::cout << "A::A(const A& other)构造函数执行了" << std::endl;
}
// 拷贝赋值运算符模板
template <typename U>
A<T1>& operator=(const A<U>& other)
{
std::cout << "A &operator=(const A &other)构造函数执行了" << std::endl;
return *this;
}
template <typename T3, typename T4>
void func(T3 val1, T4 val2) // 普通成员函数模板
{
std::cout << "A func(T3, T4) 泛化版本"
<< "type val1:" << typeid(val1).name() << "type val2:" << typeid(val1).name() << std::endl;
}
// 成员函数 偏特化
template <typename T4>
void func(int val1, T4 val2)
{
std::cout << "A func(int, T4) 偏特化版本"
<< "type val1:" << typeid(val1).name() << "type val2:" << typeid(val1).name() << std::endl;
}
// 成员函数全特化
// template <>
// void func(int val1, double val2)
// {
// std::cout << "A func(int, double)全特化版本"
// << "type val1:" << typeid(val1).name() << "type val2:" << typeid(val1).name() << std::endl;
// }
private:
T1 m_val;
};
int main(int argc, char** argv)
{
A<double> obj1;
return 0;
}
// 变量模板泛化
template <typename T>
T g_var{};
// g_var = 12.0;
// g_var = 10;
// 变量模板全特化
template <>
char g_var<double>{}; // 变量模板特化时不需要正在特化的类型(double)与这个变量模板的类型(char)保持一致
g_var<double> = 'a';
// 变量模板偏特化
template <typename T>
T g_var<T*>{120}; // T*依赖T
std::cout << g_var<int *> << std::endl;
template <typename T = int>
T g_var;
// 非类型模板参数 value
template <typename T, int value>
T g_var[value];
for (int i = 0; i < 10; ++i) {
g_var<int, 10>[i] = i;
}
for (int i = 0; i < 10; ++i) {
std::cout << g_var<int, 10>[i] << std::endl;
}
template <typename T>
struct B
{
constexpr static T value = 160;
};
template <typename T>
int g_val = B<T>::value;
std::cout << g_val<int> << std::endl; // 160
g_val<int> = 120;
std::cout << g_val<int> << std::endl; // 120
template <typename T>
class D {
public:
template <typename W>
static W m_tpi; // 静态成员变量模板声明
};
template <typename T>
template <typename W>
W D<T>::m_tpi = 5;
int main(int argc, char** argv)
{
std::cout << D<float>::m_tpi<int> << std::endl;
D<float>::m_tpi<int> = 150;
std::cout << D<float>::m_tpi<int> << std::endl;
return 0;
}
// 别名模板
template <typename T>
using str_map_t = std::map<std::string, T>;
template <typename T>
class E {
// 成员别名模板
template <typename T>
using str_map_t = std::map<std::string, T>;
};