几种模板偏特化
除了减少模板参数个数的,还有限定范围的,比如T*
指定指针类型优先使用这个偏特化版本
模板模板参数template template paramter
参数也是一个模板参数,内部可以用前一个T指定后一个模板类型,也就是实例化时不用指定第二个参数的参数,比如template<typename T, template<typename T> class Container>
。但是不能使用默认值,也就是模板模板参数自己只能有一个参数,比如list<T, alloc=Alloc>
有默认参数,不可以<string, list> m
。
可以用C++11的语法:template<typename T> using Lst=list<T, allocator<T>>
,从而可以<string, Lst> m
。
注意template<typename T, typename S = List<T>>
不是模板模板参数,因为第二个已经确定类型了,不再是模板
详见:c++11-17 模板核心知识(十二)—— 模板的模板参数 Template Template Parameters
可变参数模板varidic templates
需要注意省略号的位置:typename...
,Types...
,args...
,myFunc(std::forward<T>(args)...)
可以用sizeof...(args)
获取args
的参数个数
万能的print
函数
1 2 3 4 5 6 7 8
| void printX(){}
template<typename T, typename... Types> void printX(const T& firstArg, const Types&... args) { cout << firstArg << endl; printX(args...); }
|
使用cout
重写printf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| void printf(const char* s) { while(*s){ if(*s == '%' && *(++s) != '%'){ throw std::runtime_error("invalid format string: missing arguments"); } std::cout << *s++; } } template<typename T, typename... Args> void printf(const char* s, T value, Args... args) { while(*s){ if(*s == '%' && *(++s) != '%'){ std::cout << value; printf(++s, agrs...); } std::cout << *s++; } throw std::logic_error("extra arguments provided to printf"); }
|
万能max
函数
如果参数类型相同,其实可以直接通过大括号调用std::max({1, 2, 3})
1 2 3 4 5 6 7 8 9 10
| int maximum(int n) { return n; }
template<typename... Args> int maximum(int n, Args... args) { return std::max(n, maximum(args...)); }
|
递归继承实现tuple
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| template<typename... Valus> class tuple; template<> class tuple<>{};
template<typename Head, typename... Tail> class tuple<Head, Tail...> : private tuple<Tail...> { typedef tuple<Tail...> inherited; protected: Head m_head; public: tuple(){} tuple()(Head v, Tail... vtail) : m_head(v), inherited(vtail...){} typename Head head() { return m_head; } inherited& tail() { return *this; } }
|
输出tuple
中的元素,前后有中括号,使用逗号分隔,格式为:[a, b, c, ...]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| template<int MAX, typename... Args> struct PRINT_TUPLE<MAX, MAX, Args>{ static void print(std::ostream& os, const tuple<Args...>& t){} }
template<int IDX, int MAX, typename... Args> struct PRINT_TUPLE{ static void print(ostream& os, const tuple<Args...>& t){ os << get<IDX>(t) << (IDX+1 == MAX ? "" : ","); PRINT_TUPLE<IDX+1, MAX, Args...>::print(os, t); } }
template<typename... Args> ostream& operator<<(ostream& os, const tuple<Args...>& t){ os << "["; PRINT_TUPLE<0, sizeof...(Args), Args...>::print(os, t); return os << "]"; }
|
万能的hash
函数
使用方法:可以在使用各unordered
容器时指定模板参数Hash
,或者在std
命名空间中为自定义类型特化hash
结构体并实现size_t operator()(const MyClass& o) const noexpect
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| template<typename T> inline void hash_combine(size_t& seed, const T& val){ seed ^= std::hash<T>()(val) + 0x9e3779b9 + (seed << 6) + (seed >> 2); }
template<typeame T> inline void hash_val(size_t& seed, const T& val){ hash_combine(seed, val); }
template<typename T, typename... Types> inline void hash_val(size_t& seed, const T& val, const Types&... args){ hash_combine(seed, val); hash_val(seed, args...); }
template<typename... Types> inline size_t hash_val(const Types&... args){ size_t seed = 0; hash_val(seed, args...); return seed; }
|
不定长参数委托
模板函数参数&&
对于参数是T
或T...
的的模板函数,一般只需要写一个参数类型为T&&...
的版本,函数体内需要使用std::forward
。因为这里T&&
会根据传递的实参进行推导和引用折叠,实际形参类型可能是&
也可能是&&
,所以需要完美转发。
但是也有一种特殊情况,也就是手动进行了特化:
1 2 3 4 5 6 7
| template<typename T> void f(T&& arg) {}
int a = 0; f(a); f<int&>(a); f<int>(a);
|
第三个函数调用就是手动执行了特化,函数参数类型是int&&
(从int&& &&
折叠而来),但是传递的实参是左值。为了避免错误,这种时候最好还是显式地指明参数类型:
1 2 3 4 5
| template<typename T> void f(T arg) {}
int a = 0; f<int&>(a);
|
对于非模板函数,或者参数是xxx<T>
的模板函数,它们的参数本身不算模板,需要写参数类型分别为const xxx&
和xxx&&
的版本。后者的函数体内部需要使用std::move
进行转移。因为这里函数参数本身并不算模板,左值就是左值,右值就是右值。
成员函数指针:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| template<typename TRet, typename TClass, typename... TArgs> static TRet Func(TClass* obj, TRet(TClass::* pMemFunc)(TArgs...), TArgs... args) { if constexpr (std::is_same_v<TRet, void>) { (obj->*pMemFunc)(args...); } else { return (obj->*pMemFunc)(args...); } }
Holder h; Func(&h, &Holder::nonStatic, 2);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #define MM(TClass, FuncName)\ template<typename TRet = decltype(declval<TClass>().FuncName()),\ typename TClass, typename... TArgs>\ static TRet TClass##FuncName(TClass* obj, TRet(TClass::* pMemFunc)(TArgs...), TArgs... args)\ {\ if constexpr (std::is_same_v<TRet, void>)\ {\ (obj->*pMemFunc)(args...);\ }\ else\ {\ return (obj->*pMemFunc)(args...);\ }\ }\
MM(Holder, nonStatic)
Holder h; HoldernonStatic(&h, &Holder::nonStatic, 0);
|