template<typename T> int compare(const T &v1, const T &v2) { if(v1 < v2) return -1; if(v2 < v1) return 1; return 0; }
// 实例化出int compare(const int&, const int&) cout << compare(1,0) <<endl; // 实例化出int compare(const vector<int>&, const vector<int>&) vector<int> vec1{1,2,3}, vec2{4,5,6}; cout << compare(vec1, vec2) << endl;
// 正确:返回类型和参数类型相同 template <typename T> T foo(T* p) { T tmp = *p; // ... return tmp; }
template<unsigned N, unsigned M> int compare(const char (&p1)[N], const char (&p2)[M]) { return strcmp(p1, p2); } compare("hi", "mom");
template <typename T> inline T min(const T&, const T&);
template<typename T>int compare(const T &v1, const T &v2) { if(less<T>()(v1, v2)) return -1; if(less<T>()(v2, v1)) return 1; return 0; }
#include <vector> #include <string> #include <initializer_list> #include <memory> template<typename T> class Blob { public: typedef T value_type; typedef typename std::vector<T>::size_type size_type; Blob(); Blob(std::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(std::move(t)); } T& back(); T& operator[](size_type i); private: std::shared_ptr<std::vector<T>> data; void check(size_type i, const std::string &msg) const; };
template <typename T> return_type Blob<T>::member_name(param_list)
template <typename T> void Blob<T>::check(size_type i, const std::string &msg) const { if(i >= data->size()) throw std::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> Blob<T>::Blob() : data(std::make_shared<std::vector<T>>()){} template <typename T> Blob<T>::Blob(std::initializer_list<T> il): data(std::make_shared<std::vector<T>>(il)){}
// 实例化Blob<int>和接受初始化列表的构造函数 Blob<int> squares = {0,1,2,3,4,5,6,7,8,9}; // 实例化Blob<int>::size() const for(size_t i = 0; i != squares.size(); ++i) squares[i] = i * i; // 实例化[]运算符
实例化了Blob<int>类和它的三个成员函数和一个构造函数
如果一个成员函数没有被使用,则它不会被实例化,成员函数只有在被用到时才进行实例化,这一特性使得即使某种类型不能完全符合模板操作的要求,但是仍然能用该类型实例化类
当我们使用使用一个类模板类型时必须提供模板实参,但这一规则有一个例外。在类模板自己的作用域中,我们可以直接使用模板名而不提供实参。
// 试图访问一个不存在的元素,类抛出异常 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& operator++(); BlobPtr& operator--(); private: std::shared_ptr<std::vector<T>> check(std::size_t, const std::string&) const; std::weak_ptr<std::vector<T>> wptr; std::size_t curr; };
template <typename T> BlobPtr<T> BlobPtr<T>::operator++(int) { BlobPtr ret = *this; ++*this; return ret; }
BlobPtr<T> ret = *this;
// 前置声明,在Blob中声明友元所需要的 template <typename> class BlobPtr; template <typename> class Blob; // 运算符==中的参数所需要的 template <typename T> bool operator==(const Blob<T>&, const Blob<T>&); template <typename T> class Blob { // 每个Blob实例将访问权限授予相同类型实例化的BlobPtr和相等运算符 friend class BlobPtr<T>; friend bool operator==<T> (const Blob<T>&, const Blob<T>&); };
// 前置声明,在将模板的一个特定实例声明为友元是用到、 template <typename T> class Pal; class C { friend class Pal<C>; // 用类C实例化的Pal是C的一个友元 // Pal2的所有实例都是C的友元,这种情况无须前置声明 template <typename T> friend class Pal2; }; template <typename T> class C2 { // C2的每个实例将相同实例化的Pal声明为友元 friend class Pal<T>; // Pal的模板声明必须在作用域之内 // Pal2的所有实例都是C2的每个实例的友元,不需要前置声明 template <typename X> friend class Pal2; // Pal3是一个非模板类,它是C2所有实例的友元 friend class Pal3; // 不需要前置声明 };
template <typename Type> class Bar { friend Type; };
template<typename T> using twin = pair<T, T>; twin<string> authors; twin<int> win_loss; twin<double> area; // 当我们定义一个模板类型别名时,可以固定一个或多个模板参数 template <typename T> using partNo = pair<T, unsigned>; partNo<string> books; partNo<Student> kids;
template <typename T> class Foo { public: static std::size_t count(){return ctr;} private: static std::size_t ctr; };
template <typename T> size_t Foo<T>::ctr = 0; Foo<int> fi; // 实例化Foo<int> 类和static数据成员ctr auto ct = Foo<int>::count(); // 实例化Foo<int>::count ct = fi.count(); // 使用Foo<int>::count ct = Foo::count(); // 错误。使用哪个模板实例的count?
template <typename Foo> Foo cal(const Foo& a, const Foo& b) { Foo tmp = a; return tmp; }
typedef double A; template <typename A, typename B> void f(A a, B b) { A tmp = a; // tmp 类型为模板参数A的类型,而不是double double B; // 错误,重声明模板参数B }
template <typename V, typename V> // 错误
// 声明但不定义compare和Blob template <typename T> int compare(const T&, const T&); template <typename T> class Blob;
// 3个clac都指向相同的函数模板 template <typename T>T clac(const T&, const T&); template <typename U>U clac(const U&, const U&); template <typename Type> Type clac(const Type& a. const Type& b){...}
当然,一个给定模板的每个声明和定义都必须有相同数量和种类的参数
一个特定文件所需要的所有模板的声明通常一起放置在文件开始位置,出现于任何使用这些模板的代码之前
假设T是一个模板类型参数,当编译器遇到类似T::mem这样的代码时,它不知道mem是类型成员还是静态数据成员,直至实例化时才知道,但是为了处理模板,编译器必须知道名字是否表示一个整型。例如,假定T是一个类型参数的名字,当编译器遇到T::size_type * p; 它需要知道我们正在定义一个名为p的变量还是将名为size_type静态成员与p变量相乘。
默认情况下,假定通过作用域运算符访问的是名字不是类型。因此,如果我们希望使用一个模板类型参数的类型成员,就必须显式告诉编译器该名字是一个类型。
template <typename T> typename T::value_type top(const T& c) { if(!c.empty()) return c.back(); else return typename T::value_type(); }
它使用typename指明其返回类型并在c中没有元素时生成一个值初始化的元素返回给调用者
我们也可以提供默认模板实参,在新标准中,我们可以为函数和类模板提供默认实参。
// compare有一个默认模板实参less<T>和一个默认函数实参F() template<typename T, typename F = less<T>> int compare(const T &v1, const T &v2, F f = F()) { if(f(v1, v2)) return -1; if(f(v2, v1)) return 1; return 0; }
bool i = compare(0,42); Sales_data item1(cin), item2(cin); bool j = compare(item1, item2, compareIsbn);
template <class T = int> class Numbers { public: Numbers(T v = 0): val(v){} private: T val; }; Numbers<long double> lots_of_precision; Numbers<> average_precision;
class DebugDelete { public: DebugDelete(std::ostream &s = std::cerr):os(s){} template <typename T> void operator()(T *p) const { os<<"deleting unique_ptr"<<std::endl; delete p; } private: std::ostream &os; };
double *p = new double; DebugDelete d; d(p); // 调用DebugDelete::operator()(double*) int *ip = new int; // 在一个临时DebugDelete对象上调用operator()(int*) DebugDelete()(ip);
// 销毁p所指的对象 // 实例化DebugDelete::operator()<int>(int *) unique_ptr<int, DebugDelete> p(new int, DebugDelete()); unique_ptr<string, DebugDelete> sp(new string, DebugDelete());
template <typename T> class Blob { template <typename It> Blob(It b, It e); };
template <typename T> template <typename It> Blob<T>::Blob(It b, It e): data(std::make_shared<std::vector<T>>(b,e)){}
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>类以及接受两个int*参数的构造函数 Blob<int> a1(begin(ia),end(ia)); Blob<int> a2(vi.begin(),vi.end()); Blib<string> a3(w.begin(),w.end());
extern template declaration; // 实例化声明 template declaration; // 实例化定义 extern template class Blob<string>; // 声明 template int compare(const int&,const int &); // 定义
// 这些模板类型必须在程序的其他位置进行初始化 extern template class Blob<string>; extern template int compare(const int&,const int&); Blob<string> sa1,sa2; // 实例化出现在其他位置 // Blob<int>及其接受initializer_list的构造函数在本文件中实例化 Blob<int> a1 = {0,1,2,3,4,5,6,7,8,9}; Blob<int> a2(a1); // 拷贝构造函数在本文件中实例化 int i = compare(a1[0],a2[0]); // 实例化出现在其他位置
template int compare(const int &,const int&); template class Blob<string>; // 实例化类模板的所有成员
del ? del(p) : delete p; // del(p)需要运行时跳转到del的地址
// del在编译时绑定,直接使用调用实例化的删除器 del(p); // 无运行时额外开销
从函数实参来确定模板实参的过程被称为模板实参推断
template <typename T> T fobj(T, T); // 实参被拷贝 template <typename T> T fref(const T&, const T&); // 引用 string s1("a value"); const string s2("another value"); fobj(s1, s2); // 调用fobj(string, string);const被忽略 fref(s1, s2); // 调用fref(const string&, const string&) int a[10], b[42]; fobj(a, b); // 调用f(int*, int*); ferf(a, b); // 错误,数组类型不匹配
long lng; conpare(lng, 1024); // 不能实例化compare(long, int); // 实参类型可以不同,但必须兼容 template(typename A, typename B) int flexibleCompare(const A& v1, const B& v2) { if(v1 < v2) return -1; if(v2 < v1) return 1; return 0; } long lng; flexibleCompare(lng, 1024); // 调用flexibleCompare(long, int)
// 编译器不能推断T1,它未出现在函数列表中 template <typename T1, typename T2, typename T3> T1 sum(T2, T3); // 在本例中,没有任何函数实参的类型可以用来推断T1的类型,每次调用sum时调用者都要为T1提供一个显式模板实参 auto val3 = sum<long long>(i, lng); // 糟糕的设计,用户必须指定所有三个模板参数 template <typename T1, typename T2, typename T3> T3 alternative_sum(T2, T1); // 错误,不能推断前几个模板参数 auto val3 = alternative_sum<long long>(i, lng); // 正确,显式指定三个参数 auto val2 = alternative_sum<long long, int, long>(i, lng); long lng; compare(lng, 1024); // 错误:模板参数不匹配 compare<long>(lng, 1024); // 正确,实例化compare(long, long) compare<int>(lng, 1024); // 正确,实例化compare(int, int),实现类型转换
template <typename It> auto fcn(It beg, It end) -> decltype(*beg) { return *beg; }
remove_reference<decltype(*beg)>::type;
template <typename It> auto fcn2(It beg, It end) -> typename remove_reference<decltype(*beg)>::type { // 处理序列 return *beg; }
对Mod<T>,其中Mod为 | 若T为 | 则Mod<T>::type为 |
---|---|---|
remove_reference | X& 或 X&& | X |
add_const | X& const X 或函数为T,否则 | consf T |
add_lvalue_reference | X&& 否则 | X& T& |
add_rvalue_reference | X& 或X&& 否则 | T T&& |
remove_pointer | X* | X |
add_pointer | X& X&& 否则 | X* T* |
make_signed | unsigned X | X |
make_unsigned | 带符号类型 | unsigned X |
remove_extent | X[n] | X |
remove_all_extents | X[n1][n2] | X |
template <typename T> int compare(const T&, const T&); // pf1指向实例int compare(const int&, const int&) int (*pf1)(const int&, const int&) = compare;
// func的重载版本,每个版本接受一个不同的函数指针类型 void func(int(*)(const string&, const string&)); void func(int(*)(const int&, const int&)); func(compare); // 错误,使用哪个compare呢
func(compare<int>);
template <typename T> void f1(T&); // 实参必须是一个左值 f1(i); // T:int f1(ci); // T:const int f1(5); // 错误 template <typename T> void f2(const T&); // 都被推断为const int&,f2 de const和实参中的const无关 f2(i); f2(ci); f2(5); // const&可以绑定右值 template <typename T> void f3(T&&); f3(42);
template <typename T> void f3(T&& val) { T t = val; // 拷贝还是绑定一个引用 t = fcn(t); // 赋值只改变t还是既改变t又改变val if(val == t){} // 若T是引用类型,则一直为true }
template <typename T> void f(T&&); // 绑定非const右值 template <typename T> void f(const T&); // 左值和const右值
// 在返回类型和类型转换中也要用到typename template <typename T> typename remove_reference<T>::type&& move(T&& t) { return static_cast<typename remove_reference<T>::type&&>(t); }
在std::move(string(“bye”))中
- 推断出T的类型为string
- remove_reference用string进行实例化
- remove_reference<string>的type成员是string
- move的返回类型是string&&
- move的函数参数t的类型为string&&
- 实例化为:string&& move(string &&t)
- 函数体返回static_cast<string&&>(t)。什么也不做
std::move(s1);
- 推断出T的类型是string&
- remove_reference用string&进行实例化
- type成员是string
- move返回类型是string&&
- move的函数参数t实例化为string& &&,折叠为string&
- 实例化为:string&& move(string &t)
- static_cast将左值引用转换为右值引用
// flip1是一个不完整实现,顶层const和引用丢失了 template <typename F, typename T1, typename T2> void flip1(F f, T1 t1, T2 t2) { f(t2,t1); } // 这个函数一般工作正常,但是如果调用一个接受引用参数的函数时就会出现问题 void f(int v1, int &v2) { cout << v1 << " " << ++v2 << endl; }
template <typename F, typename T1, typename T2> void flip2(F f, T1 &&t1, T2 &&t2) { f(t2, t1); }
void g(int &&i, int &j) { cout << i << " " << j << endl; } flip2(g, i, 42); // 错误,不能从一个左值实例化int&&
template <typename Type> intermediary(Type &&arg) { finalFcn(std::forward<Type>(arg)); // ... }
template <typename F, typename T1, typename T2> void flip(F f, T1 &&t1, T2 &&t2) { f(std::forward<T2>(t2), std::forward<T1>(t1)); }
如果涉及函数模板,则函数匹配规则会在几个方面受到影响
// 打印任何我们不能处理的类型 template <typename T> string debug_rep(const T &t) { ostringstream ret; ret << t; return ret.str(); // 返回ret绑定的string的一个副本 } // 打印指针的值,后跟着指针所指的对象 // 注意:不能用于char* 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(); } string s("hi"); cout << debug_rep(s) << endl; cout << debug_rep(&s) << endl;
// 考虑另一个调用 const string *sp = &s; cout << debug_rep(sp) << endl;
// 打印双引号包围的string string debug_rep(const string &s) { return '"' + s + '"'; } string s("hi"); cout << debug_rep(s) << endl;
// 考虑这个调用 cout << debug_rep("hi world!") << endl;
string debug_rep(char *p) { return debug_rep(string(p)); } string debug_rep(const char *p) { return debug_rep(string(p)); }
template <typename T> string debug_rep(const T &t); template <typename T> string debug_rep(T *p); // 下面的声明必须在作用域中 string debug_rep(const string&); string debug_rep(char *p) { return debug_rep(string(p)); }
// Args是一个模板参数包,rest是一个函数参数包 // Args表示0个或多个模板类型参数 // rest表示0个或多个函数参数 template <typename T, typename... Args> void foo(const T &t, const Args&... rest);
int i = 0; double d = 3.14; string s = "how now brown cow"; foo(i, s, 42, d); // 包中有三个参数 foo(s, 42, "hi"); foo(d, s); foo("hi"); // 空包 // 编译器会为foo实例化出四个不同的版本: void foo(const int&, const string&, const int&, const double&); void foo(const string&, const int &, const char[3]&); void foo(const double&, const string&); void foo(const char[3]&); 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&... rest) { os << t << ", "; return print(os, rest...); }
// 在print调用中对每个实参调用debug_rep template <typename... Args> ostream &errorMsg(ostream &os, const Args&... rest) { return print(os, debug_rep(rest)...); }
class StrVec { public: template <class... Args> void emplace_back(Args&&...); };
template <class... Args> inline void StrVec::emplace_back(Args&&... args) { chk_n_alloc(); alloc.construct(first_free++, std::forward<Args>(args)...); }
template<typename... Args> void fun(Args&&... args) { work(std::forward<Args>(args)...); }
// 第一个版本,比较任意两个类型 template <typename T> int compare(const T&, const T&); // 处理字符串字面常量 template <size_t N, size_t M> int compare(const char(&)[N], const char(&)[M]);
template <> int compare(const char* const &p1, const char* const &p2) { return strcmp(p1, p2); }
namespace std { template<> struct hash<Sales_data> { typedef size_t result_type; typedef Sales_data argument_type; size_t operator()(const Sales_data& s)const; }; size_t hash<Sales_data>::operator()(const Sales_data& s)const { return hash<string>()(s.bookNo) ^ hash<unsigned>()(s.units_sold) ^ hash<double>()(s.revenue); } }
// 使用hash<Sales_data>和Sales_data中的operator== unordered_multiset<Sales_data> SDset;
// 原始的通用的版本 template <class T> struct remove_reference { typedef T type; }; // 部分特例化版本,将用于左值引用和右值引用 template <class T> struct remove_reference<T&> {typedef T type;}; template <class T> struct remove_reference<T&&> {typedef T type;};
template <typename T> struct Foo { Foo(const T &t = T()):mem(t){} void Bar(){} T mem; }; template<> void Foo<int>::Bar() { // 进行应用于int的特例化处理 }