一個有趣的東西:實現一個函數print, 輸入一個數組, 輸出數組的各個維度長度。
eg. int a[2], b[3][4], c[5][6][7]; print(a); //(2, 4) print(b); //(3, 16) (4, 4) print(c); //(5, 168) (6, 28) (7, 4)

template<typename T> void print(const T &A) { printf("\n"); } template<typename T, int N> void print(const T (&A)[N]) { printf("(%d, %d) ", N, sizeof(A[0])); print(A[0]); }
學習版塊
https://github.com/wuye9036/CppTemplateTutorial 空明流轉
typename與class

在 C++ Template 中很多地方都用到了 typename 與 class 這兩個關鍵字,而且好像可以替換,是不是這兩個關鍵字完全一樣呢? 相信學習 C++ 的人對 class 這個關鍵字都非常明白,class 用於定義類,在模板引入 c++ 后,最初定義模板的方法為: template<class T>...... 這里 class 關鍵字表明T是一個類型,后來為了避免 class 在這兩個地方的使用可能給人帶來混淆,所以引入了 typename 這個關鍵字,它的作用同 class 一樣表明后面的符號為一個類型,這樣在定義模板的時候就可以使用下面的方式了: template<typename T>...... 在模板定義語法中關鍵字 class 與 typename 的作用完全一樣。 typename 難道僅僅在模板定義中起作用嗎?其實不是這樣,typename 另外一個作用為:使用嵌套依賴類型(nested depended name),如下所示: class MyArray { public: typedef int LengthType; ..... } template<class T> void MyMethod( T myarr ) { typedef typename T::LengthType LengthType; LengthType length = myarr.GetLength; } 這個時候 typename 的作用就是告訴 c++ 編譯器,typename 后面的字符串為一個類型名稱,而不是成員函數或者成員變量,這個時候如果前面沒有 typename,編譯器沒有任何辦法知道 T::LengthType 是一個類型還是一個成員名稱(靜態數據成員或者靜態函數),所以編譯不能夠通過。
eg1.

1 template <typename T> struct X {}; 2 template <typename T> struct Y 3 { 4 // X可以查找到原型; 5 // X<T>是一個依賴性名稱,模板定義階段並不管X<T>是不是正確的。 6 typedef X<T> ReboundType; 7 8 // X可以查找到原型; 9 // X<T>是一個依賴性名稱,X<T>::MemberType也是一個依賴性名稱; 10 // 所以模板聲明時也不會管X模板里面有沒有MemberType這回事。 11 typedef typename X<T>::MemberType MemberType2; 12 13 // UnknownType 不是一個依賴性名稱 14 // 而且這個名字在當前作用域中不存在,所以直接報錯。 15 // typedef UnknownType MemberType3; 16 17 void foo() 18 { 19 X<T> instance0; 20 typename X<T>::MemberType instance1; 21 } 22 };
eg2.

1 struct A; 2 template <typename T> struct B; 3 template <typename T> struct X { 4 typedef X<T> _A; // 編譯器當然知道 X<T> 是一個類型。 5 typedef X _B; // X 等價於 X<T> 的縮寫 6 typedef T _C; // T 不是一個類型還玩毛 7 8 // !!!注意我要變形了!!! 9 class Y { 10 typedef X<T> _D; // X 的內部,既然外部高枕無憂,內部更不用說了 11 typedef X<T>::Y _E; // 嗯,這里也沒問題,編譯器知道Y就是當前的類型, 12 // 這里在VS2015上會有錯,需要添加 typename, 13 // Clang 上順利通過。 14 typedef typename X<T*>::Y _F; // 這個居然要加 typename! 15 // 因為,X<T*>和X<T>不一樣哦, 16 // 它可能會在實例化的時候被別的偏特化給搶過去實現了。 17 }; 18 19 typedef A _G; // 嗯,沒問題,A在外面聲明啦 20 typedef B<T> _H; // B<T>也是一個類型 21 typedef typename B<T>::type _I; // 嗯,因為不知道B<T>::type的信息, 22 // 所以需要typename 23 //typedef B<int>::type _J; // B<int> 不依賴模板參數, 24 // 所以編譯器直接就實例化(instantiate)了 25 // 但是這個時候,B並沒有被實現,所以就出錯了 26 };
自己的理解:什么時候需要typename?如果編譯器無法判斷當前名稱代表的是類型還是實例的時候,需要用typename來表示指代類型。如果遇到T::size_type * p; 無法知道是乘法運算還是定義指針。這時候,當我們希望通知編譯器一個名字表示類型時,必須用關鍵字typename,而不是class。則為typename T::size_type *p;
============================分割線============================
引用折疊
引用折疊只能應用於間接創建的引用的引用,如類型別名或模板參數。
& &&,&& &,& &折疊后都是左值引用&; && &&折疊后是右值引用&&。
模板實參推斷與引用
其中i是int,ci是const int.
template<typename T> void f1(T&); // 實參必須是左值,const屬性得到保持
f1(i); //T是int
f1(ci); //T是const int
f1(5); //error, 必須是左值
template<typename T> void f2(const T&); //可以接受右值
f2(i);
f2(ci);
f2(5); //以上T均為int
template<typename T> void f3(T&&); //實參為左值,T為左值引用;實參為const左值,T為const左值引用;實參為右值,T為右值引用
f3(i); //T是int&
f3(ci); //T是const int&
f3(5); //T是int&&
T&&,對應的const屬性和左值/右值屬性將得到保持。T&&可以實現轉發,保持轉發參數的所有性質,包括const和左右值。
移動與完美轉發
std::move 如下.
/// remove_reference template<typename _Tp> struct remove_reference { typedef _Tp type; }; template<typename _Tp> struct remove_reference<_Tp&> { typedef _Tp type; }; template<typename _Tp> struct remove_reference<_Tp&&> { typedef _Tp type; }; template <typename T> typename remove_reference<T>::type&& move(T&& t) { return static_cast<typename remove_reference<T>::type&&>(t); }
std::forward.
template <typename T> T&& forward(typename remove_reference<T>::type& param) { return static_cast<T&&>(param); }
eg.
template<typename T> void foo(T&& fparam) { std::forward<T>(fprams); } int i = 7; foo(i);//T為int&,std::forward<T>(fprams)的類型為int & &&,折疊后為int & foo(47);//T為int, std::forward<T>(fprams)的類型為int && //通過完美轉發,可以簡化代碼,其中m_var1和m_var2是全局變量 void set(const string & var1, const string & var2) { //拷貝賦值 m_var1 = var1; m_var2 = var2; } void set(string && var1, string && var2){ //移動賦值 m_var1 = std::move(var1);//move不能省略,具名變量都被當作左值 m_var2 = std::move(var2); } //以上兩個函數可以用以下函數代替 template<typename T1, typename T2> void set(T1 && var1, T2 && var2){ m_var1 = std::forward<T1>(var1); m_var2 = std::forward<T2>(var2); } //注:forward常用於template函數中,必須要多帶一個參數forward<T>
可變參數函數模板(variadic function template)
主要使用了包擴展(pack expansion)的方式, 即"...", 把一個模式(pattern)擴展為包中每一個元素(element)的形式;
可變參數函數模板, 常使用遞歸(recursive)進行處理包(pack)參數,
需要一個非可變參數(nonvariadic)函數模板,結束遞歸, 當最后一次調用時, 調用非可變參數版本, 則遞歸結束;
還需要一個綁定(bing)的參數, 處理包(pack)中的一些元素, 通過遞歸,順次處理包中的元素;
包擴展可以應用於各種形式, 如
函數的模板參數, 例如: templae<typename... Args> 擴展后即 template<typename Args1, typename Args2, typename Args3>
函數的參數模板, 例如: cosnt Args&... rest 擴展后即const Args& rest1, const Args& rest2, const Args& rest3
函數的形參, 例如: rest... 擴展后即rest1, rest2, rest3
函數模板, 例如: debug_rep(rest)...擴展后即debug_rep(rest1), debug_rep(rest2), debug_rep(rest3) //debug_rep是模板函數名
eg.
1 #include <iostream> 2 #include <sstream> 3 4 using namespace std; 5 6 //返回bug信息 7 template <typename T> 8 std::string debug_rep (const T& t) 9 { 10 std::ostringstream ret; 11 ret << t; 12 return ret.str(); 13 } 14 15 //非可變參數模板 16 template<typename T> 17 std::ostream &print(std::ostream &os, const T &t) 18 { 19 //std::cout << "This is nonvariadic function! "; 20 return os << t; 21 } 22 23 //可變參數模板, "..."是包擴展(Pack Expansion) 24 template <typename T, typename... Args> 25 std::ostream &print(std::ostream &os, const T &t, const Args&... rest) 26 { 27 os << t << ", "; 28 return print(os, rest...); 29 } 30 31 //函數模板的包擴展 32 template <typename... Args> 33 std::ostream &errorMsg(std::ostream &os, const Args&... rest) 34 { 35 return print(os, debug_rep(rest)...); //使用模板的包擴展 36 } 37 38 int main() 39 { 40 int i(10); std::string s("girls"); 41 //print(std::cout, i, s, 42); 42 errorMsg(std::cout, i, s, 10, "ladies"); 43 44 return 0; 45 }
C++11中的tuple就是可變參數類模板,在github找到了一份實現代碼。(這個人好像還實現了好多別的東西,比如紅黑樹,堆排序,哈希表)
1 #include <iostream> 2 3 template<typename ... Types> class Tuple; 4 template<> class Tuple<> {}; 5 template<typename First, typename ... Rest> 6 class Tuple<First, Rest...>: private Tuple<Rest...> { 7 First Member; 8 public: 9 Tuple(const First& first, const Rest& ... rest): 10 Tuple<Rest...>(rest...), Member(first) {} 11 12 const First& Head() const { 13 return Member; 14 } 15 16 const Tuple<Rest...>& Tail() const { 17 return *this; 18 } 19 }; 20 21 22 // As a return value of Get<>(Tuple) method is not a reference, 23 // in order to make it available to return POD types. 24 25 template<size_t I, class T> 26 struct TupleElement; 27 28 template<size_t I, class Head, class ... Rest> 29 struct TupleElement<I, Tuple<Head, Rest...> >: 30 public TupleElement<I - 1, Tuple<Rest...> > { 31 static typename TupleElement<I, Tuple<Head, Rest...> >::Type 32 Get(const Tuple<Head, Rest...>& t) { 33 return TupleElement<I - 1, Tuple<Rest...> >::Get(t.Tail()); 34 } 35 }; 36 37 template<class Head, class ... Rest> 38 struct TupleElement<0, Tuple<Head, Rest...> > { 39 typedef Head Type; 40 static typename TupleElement<0, Tuple<Head, Rest...> >::Type 41 Get(const Tuple<Head, Rest...>& t) { 42 return t.Head(); 43 } 44 }; 45 46 template<size_t Pos, class Head, class ... Rest> 47 typename TupleElement<Pos, Tuple<Head, Rest...> >::Type 48 Get(const Tuple<Head, Rest...>& t) { 49 return TupleElement<Pos, Tuple<Head, Rest...> >::Get(t); 50 } 51 52 int main() { 53 Tuple<int, double, char> t(42, 3.14, 'a'); 54 std::cout << Get<0>(t) << std::endl; 55 std::cout << Get<1>(t) << std::endl; 56 std::cout << Get<2>(t) << std::endl; 57 return 0; 58 }
轉發參數包
組合使用forward機制與可變參數模板實現轉發參數包。《C++ primer 5th》16.4.3
============================分割線============================
模板元編程
(黑魔法:編譯期間完成計算,如斐波那契,平方根等)
eg.斐波那契等
1 #include <bits/stdc++.h> 2 3 template<int N> 4 struct Fac{ 5 static int const result = Fac<N-1>::result + Fac<N-2>::result; 6 }; 7 template<> 8 struct Fac<0>{ 9 static int const result = 0; 10 }; 11 template<> 12 struct Fac<1>{ 13 static int const result = 1; 14 }; 15 /* 16 推薦使用enum 17 靜態成員變量只能是左值 18 如果遇到void foo(int const&); 19 foo(Fac<7>::result); 20 編譯器將傳遞Fac<7>::result的地址, 21 會強制編譯期實例化靜態成員的定義, 22 並為該定義分配內存, 23 於是該計算將不局限於完全的“編譯期”效果 24 而enum不是左值, 會以常量的形式傳遞參數 25 以上抄自<<C++ template(中文版)>>P296 26 */ 27 28 template<int N> 29 struct Sum{ 30 enum{result = N+Sum<N-1>::result}; 31 }; 32 template<> 33 struct Sum<0>{ 34 enum{result = 0}; 35 }; 36 37 int main(){ 38 std::cout << Fac<46>::result << std::endl; 39 //std::cout << Fac<47>::result << std::endl; // without Fac<46>, compile error 40 std::cout << Sum<900>::result << std::endl; 41 //std:cout << Sum<901>::result << std::endl; // without Sum<900>, compile error 42 std::cout << Sum<1800>::result << std::endl; // OK 43 //std::cout << Sum<1801>::result << std::endl; // without Sum<1800>, compile error 44 return 0; 45 }
eg. Sqrt簡易版本
1 //easy版本, :?條件表達式會同時實例化Sqrt<20, 0, 9>, Sqrt<20, 10, 20> 2 #include <bits/stdc++.h> 3 template<int N, int L = 0, int R = N> 4 class Sqrt{ 5 public: 6 enum{m = (L+R+1) >> 1}; 7 enum{result = m*m > N? Sqrt<N, L, m-1>::result : Sqrt<N, m, R>::result}; 8 }; 9 template<int N, int L> 10 class Sqrt<N, L, L>{ 11 public: 12 enum{result = L}; 13 }; 14 15 int main(){ 16 std::cout << Sqrt<20>::result; 17 return 0; 18 }
tips:可以通過typedef+IfThenElse模板優化條件表達式
1 #include <bits/stdc++.h> 2 3 #ifndef IFTHENELSE 4 #define IFTHENELSE 5 template<bool C, typename Ta, typename Tb> 6 class IfThenElse; 7 8 //局部特化 9 template<typename Ta, typename Tb> 10 class IfThenElse<true, Ta, Tb>{ 11 public: 12 typedef Ta Result; 13 }; 14 template<typename Ta, typename Tb> 15 class IfThenElse<false, Ta, Tb>{ 16 public: 17 typedef Tb Result; 18 }; 19 #endif 20 21 template<int N, int L = 0, int R = N> 22 class Sqrt{ 23 public: 24 enum{m = (L+R+1) >> 1}; 25 typedef typename IfThenElse< 26 (m*m > N), 27 Sqrt<N, L, m-1>, 28 Sqrt<N, m, R> 29 >::Result SubT; //定義typedef不會實例化 30 enum{result = SubT::result}; 31 }; 32 template<int N, int L> 33 class Sqrt<N, L, L>{ 34 public: 35 enum{result = L}; 36 }; 37 38 int main(){ 39 std::cout << Sqrt<20>::result; 40 return 0; 41 }
迭代版本
1 #include <bits/stdc++.h> 2 3 #ifndef IFTHENELSE 4 #define IFTHENELSE 5 template<bool C, typename Ta, typename Tb> 6 class IfThenElse; 7 8 //局部特化 9 template<typename Ta, typename Tb> 10 class IfThenElse<true, Ta, Tb>{ 11 public: 12 typedef Ta Result; 13 }; 14 template<typename Ta, typename Tb> 15 class IfThenElse<false, Ta, Tb>{ 16 public: 17 typedef Tb Result; 18 }; 19 #endif 20 21 template<int N> 22 class Value{ 23 public: 24 enum{result = N}; 25 }; 26 template<int N, int I = 0> 27 class Sqrt{ 28 public: 29 typedef typename IfThenElse< 30 ((I+1)*(I+1) > N), 31 Value<I>, //i, 特意構造一個Value類 32 Sqrt<N, I+1> 33 >::Result SubT; 34 35 enum{result = SubT::result}; 36 }; 37 38 int main(){ 39 std::cout << Sqrt<20>::result; 40 return 0; 41 }
未完待續