一、模板定义以关键字template开始,后跟一个模板参数列表,模板参数列表以逗号分隔,用小于号和大于号包围起来
二、模板参数前使用关键字class或typename
三、一个非类型参数表示一个值而非一个类型,通过一个特定的类型名而非关键字class或typename
四、一个非类型参数可以是一个整型,或者是一个指向对象或函数类型的指针或引用。绑定到非类型整型参数的实参必须是一个常量表达式。绑定到指针或引用非类型参数的实参必须具有静态的生存期
五、函数模板可以声明为inline或constexpr,放在模板参数列表之后,返回类型之前
六、为了生成一个实例化版本,编译器需要掌握函数模板或类模板成员函数的定义,所以模板的头文件通常包括声明和定义
七、编译器不能为类模板推断模板参数类型
八、定义在类模板内的成员函数被隐式声明为内联函数
九、定义在类模板之外的成员函数必须以关键字template开始,后跟类模板参数列表
十、在类模板自己的作用域中,可以直接使用模板名而不提供实参
十一、当一个类包含一个友元声明时,类与友元各自是否是模板是相互无关的。如果一个类模板包含一个非模板友元,则友元被授权可以访问所有模板实例。如果友元自身是模板,类可以授权给所有友元模板实例,也可以只授权给特定实例
十二、模板声明必须包含模板参数
十三、如果希望使用一个模板类型参数的类型成员,就必须显式告诉编译器该名字是一个类型,通过使用关键字typename来实现
十四、如果一个类模板为其所有模板参数都提供了默认实参,且我们希望使用这些默认实参,就必须在模板名后跟一对空尖括号
十五、一个类可以包含本身是模板的成员函数,称为成员模板,成员模板不能是虚函数
十六、当在类模板外定义一个成员模板时,必须同时为类模板和成员模板提供模板参数列表。类模板的参数列表在前,后面跟成员自己的模板参数列表
十七、显式实例化声明: extern template declaration
十八、一个类模板的实例化定义会实例化该模板的所有成员,包括内联的成员函数
十九、显式模板实参在尖括号中给出,位于函数名之后,实参列表之前
二十、可以使用remove_reference来获取元素类型,remove_reference模板有一个模板类型参数和一个名为type的类型成员。如果用一个引用类型实例化remove_reference,则type表示被引用的类型:remove_reference<decltype(*beg)>::type. decltype(*beg)返回元素类型的引用类型,remove_reference::type脱去引用,剩下元素类型本身
二一、当一个函数参数是模板类型参数的一个普通引用时,只能传递给它一个左值,实参可以时const类型,也可以不是; 当一个函数参数的类型是const T&,可以传递给它任何类型的实参;当一个函数参数是一个右值引用,可以传递给它一个右值
二二、通常不能将一个右值引用绑定到一个左值上,但有2个例外:
1、当经一个左值传递给函数的右值引用参数,且此右值引用指向模板类型参数时,编译器推断模板类型参数为实参的左值引用类型
2、如果间接拆功能键一个引用的引用,则这些引用形成折叠,引用折叠成一个普通的左值引用类型,新标准可以折叠成右值引用,即右值引用的右值引用
(1)、T& &,T& &&, T&& &都折叠成类型T&
(2)、类型T&& &&折叠成T&&
二三、实际开发中,右值引用通常用于2中情况: 模板转发其实参或模板被重载
二四、虽然不能隐式的将一个左值转换为右值引用,但可以通过static_cast显式的将要给左值转换为一个右值引用
二五、通常情况下,使用forward传递传递那些定义为模板类型参数的右值引用的函数参数,通过其返回类型上的引用折叠,forward可以保持给定实参的左值/右值属性
二六、函数模板可以被另一个模板或一个普通非模板函数重载
二七、一个可变参数模板就是一个接受可变数目参数的模板函数或模板类,可变数目的参数被称为参数包。存在2中参数包: 模板参数包;函数参数包
二八 、用一个省略号来指出一个模板参数或函数参数表示一个包。在一个模板参数列表中,class...或typename...指出接下来的参数表示0个或多个类型的列表;一个类型名后面跟一个省略号表示0个或多个给定类型的非类型参数的类别
二九、当需要知道包中有多少给元素时,可以使用sizeof...运算符,类似sizeof运算符,sizeof...也返回一个常量表达式,且不会对其实参求值
三十、可以组合使用可变参数模板与forward机制来实现将其实参不变的传递给其他函数
三一、当特例化一个函数模板时,必须为原模板中的每个模板参数都提供实参,为了指出正在实例化一个模板,应使用关键字template后跟一个空尖括号对,空尖括号指出将为原模板的所有模板参数提供实参
三二、当定义一个特例化版本时,函数参数类型必须与一个先前声明的模板中对应的类型匹配
三三、一个特例化版本本质上时一个实例而非函数名的一个重载版本
三四、可以向命名空间添加成员,首先需要打开命名空间: namespace std {}
template<typename T>
int compare(const T& v1, const T& v2)
{
//if (v1 < v2) return -1;
//if (v2 < v1) return 1;
if (less<T>() (v1, v2)) return -1;
if (less<T>() (v2, v1)) return 1;
return 0;
}
template<typename T, typename F = less<T>>
int compare2(const T& v1, const T& v2, F f = f())
{
if (f(v1, v2)) return -1;
if (f(v2, v1)) return 1;
return 0;
}
template<unsigned N, unsigned M>
int compare1(const char(&p1)[N], const char(&p2)[M])
{
return strcmp(p1, p2);
}
//函数模板特例化, 特例化compare(const T& v1, const T& v2)
template<>
int compare(const char* const &p1, const char* const &p2)
{
}
template<typename> class BlobPtr;
template<typename> class Blob;
template<typename T> bool operator==(const Blob<T>&, const Blob<T>&);
template<typename T>
class Blob
{
friend class BlobPtr<T>;
friend bool operator==<T>(const Blob<T>&, const Blob<T>&);
public:
typedef T value_type;
typedef typename vector<T>::size_type size_type;
// 类模板的成员模板
template <typename I>
Blob(I b, I e);
Blob();
Blob(initializer_list<T> il);
size_type size() const { return data->size(); }
bool empty() const { return data->empty(); }
void push_back(const T& t) { data->push_back(t); }
void push_back(T& t) { data->push_back(move(t)); }
void pop_back();
T& back();
T& operator[](size_type i);
private:
shared_ptr<vector<T>> data;
void check(size_type i, const string& msg) const;
};
template<typename T>
void Blob<T>::check(size_type i, const string& msg) const
{
if (i >= data->size()) {
throw out_of_range(msg);
}
}
template<typename T>
T& Blob<T>::back()
{
check(0, "back on empty Blob");
return data->back();
}
template<typename T>
T& Blob<T>::operator[](size_type i)
{
check(i, "subscript out of range");
return (*data)[i];
}
template<typename T>
void Blob<T>::pop_back()
{
check(0, "pop_back on empty Blob");
return data->pop_back();
}
// 类模板的参数列表在前,后跟成员自己的模板参数列表
template <typename T>
template<typename I>
Blob<T>::Blob(I b, I e) : data(make_shared<vector<T>>(b, e))
{
}
template<typename T>
Blob<T>::Blob() : data(make_shared<vector<T>>())
{
}
template<typename T>
Blob<T>::Blob(initializer_list<T> il) : data(make_shared<vector<T>>(il))
{
}
template <typename T>
class BlobPtr
{
public:
BlobPtr() : curr(0) {}
BlobPtr(Blob<T>& a, size_t sz = 0) : wptr(a.data), curr(sz) {}
T& operator*() const
{
auto p = check(curr, "dereference past end");
return (*p)[curr];
}
//BlobPtr<T>& operator++();
BlobPtr& operator++(); // 在类模板自己的作用域中,可以直接使用模板名而不提供实参
BlobPtr& operator--();
private:
shared_ptr<vector<T>> check(size_t, const string&) const;
weak_ptr<vector<T>> wptr;
size_t curr;
};
template<typename T>
typename T::value_type top(const T& c)
{
if (!c.empty()) {
return c.back();
}
return typename T::value_type();
}
template<class T= int>
class Numbers
{
public:
Numbers(T v = 0) : val(v) {}
private:
T val;
};
class DebugDelete
{
public:
DebugDelete(ostream& s = cerr) : os(s) {}
// 普通类的成员模板
template<typename T>
void operator()(T* p) const
{
os << "deleting unique_ptr" << endl;
delete p;
}
private:
ostream& os;
};
// 尾置返回类型
template <typename T>
auto fcn(T beg, T end) ->decltype(*beg)
{
return *beg;
}
// decltype(*beg)返回元素类型的引用类型,remove_reference::type脱去引用,剩下元素类型本身
template <typename T>
auto fcn2(T beg, T end) ->typename remove_reference<decltype(*beg)>::type
{
return *beg;
}
// 当一个函数参数是模板类型参数的一个普通引用时,只能传递给它一个左值,可以是const的,也可以不是
template<typename T>
void f1(T&);
//f1(intA); // 正确, 模板参数T是int
//f1(constIntA); // 正确,模板参数T是const int
//f1(5);// 错误, 传递给一个&参数的实参必须是一个左值
// 一个函数参数的类型是const T&,可以传递给它任何类型的实参
template<typename T>
void f2(const T&);
//f2(intA); // 正确, 模板参数T是int
//f2(constIntA); // 正确,模板参数T是const int
//f2(5);// 正确, 一个const&参数可以绑定到一个右值,T是int
// 当一个函数参数是一个右值引用,可以传递给它一个右值
template<typename T>
void f3(const T&&);
//f3(5); // 正确,实参是一个int类型的右值,模板参数T是int
// move定义
template<typename T>
typename remove_reference<T>::type&& move(T&& t)
{
return static_cast<typename remove_reference<T>::type&&>(t);
}
// 模板重载
template<typename T>
string debug_rep(const T& t)
{
ostringstream ret;
ret << t;
return ret.str();
}
template<typename T>
string debug_rep(T* p)
{
ostringstream ret;
ret << "pointer:" << p;
if (p) {
ret << " " << debug_rep(*p);
}
else {
ret << " null pointer";
}
return ret.str();
}
// 可变参数模板
template<typename T, typename... Args>
void foo(const T& t, const Args&... args);
template<typename... Args>
void g(Args... args)
{
cout << sizeof...(Args) << endl; // 类型参数的数目
cout << sizeof...(args) << endl; // 函数参数的数目
}
// 用来终止递归并打印最后一个元素
template<typename T>
ostream& print(ostream& os, const T& t)
{
return os << t;
}
template<typename T, typename... Args>
ostream& print(ostream& os, const T& t, const Args&... args)
{
os << t << ", ";
return print(os, args...);
}
// 可以向命名空间添加成员
namespace std {
template<>
struct hash<int>
{
};
}
// 显式实例化
extern template class Blob<string>;
extern template int compare(const int&, const int&);
int main()
{
cout << compare(1, 0) << endl;
cout << compare1("hi", "mom");
Blob<int> squares = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
for (size_t i = 0; i != squares.size(); ++i) {
squares[i] = i * i;
}
Numbers<long double> lots;
Numbers<> log; // 空<>表示使用默认类型
// 普通类的成员模板
double* p = new double;
DebugDelete d;
d(p);
int* ip = new int;
DebugDelete()(ip);
// 类模板的成员模板
int ia[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
vector<long> vi = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
list<const char*> w = { "now", "is", "the", "time" };
Blob<int> a1(begin(ia), end(ia));
Blob<int> a2(vi.begin(), vi.end());
Blob<string> a3(w.begin(), w.end());
// 显式实例化
Blob<string> sa1, sa2;
Blob<int> a5;
int i = compare(1, 2);
int j = compare<long>(1.1, 2.2); // 显式模板实参
int fi = 0;
double fd = 3.14;
string fs = "how now brown cow";
foo(fi, fs, 43, fd); // 可变参数包含3个参数
foo(fd, fs);// 可变参数包含1个参数
foo(fd);// 空包
print(cout, fi, fs, 42);
// 第一次 print(cout, fi, fs, 42) t: fi, args..: fs, 42
// 第二次 print(cout, fs, 42) t: fs, args..: 42
// 第三次 print(cout, 42) 调用非可变参数版本的print
return 0;
}