模板的模板參數

模板的模板參數(template template parameter)是指C++語言程序設計中,一個模板的參數是模板類型。只有類模板允許其模板參數是模板類型;函數模板不允許具有這樣的模板參數(此處有爭議[1])。

模板的模板參數可以有默認值。[2]例如:

template <class T = float> struct B {};
template <template <class TT = float> class T> struct A {
     inline void f();
     inline void g();
};
template <template <class TT> class T> void A<T>::f() {
      T<> t; // error - TT has no default template argument
}
template <template <class TT = char> class T> void A<T>::g() {
       T<> t; // OK - T<char>
}

模板的模板參數,其實參應當是類模板名字或者別名模板(alias template)。當模板的模板參數做「形參實參結合」時,僅考慮把實參的基本(即未特化)類模板與模板的模板形參做匹配;不考慮實參的偏特化類模板,即使偏特化後的參數列表與模板的模板形參匹配得上。[3] 例如:

template <typename T, template <typename ELEM, typename ALLOC = std::allocator<ELEM> > 
                         class CONT = std::deque>   //deque的基本型有2个模板参数 
class Stack { 
  private: 
    CONT<T> elems;         // OK!
    //… 
}; 

當模板的模板參數被實例化時,考慮採用該模板參數的偏特化版本。如果在實例化之處該偏特化版本仍不可見,且如果它是可見的則應該當採用,這時該程序為病態。[4] 例如:

template<class T> class A { // primary template
      int x;
};
template<class T> class A<T*> { // partial specialization
       long x;
};
template<template<class U> class V> class C {
        V<int> y;
        V<int*> z;
};
C<A> c; // V<int> within C<A> uses the primary template, so c.y.x has type int
        // V<int*> within C<A> uses the partial specialization, so c.z.x has type long

當一個模板的模板形參(稱作P)與一個作為實參的模板(稱作A)匹配時, 要求P與A各自的模板形參列表的對應成員滿足:

  • 具有相同種類(類型參數、非類型參數、模板作為參數);
  • 如果二者是非類型的模板參數,則其具體類型應相等;
  • 如果二者是模板作為參數,則遞歸執行上述模板的模板參數形實匹配規則;
  • 如果P是一個可變參數模板,即P的模板形參列表包含了模板參數包(template parameter pack),則這個模板參數包應該匹配A中0個或者多個模板參數或者匹配A中的對應的模板參數包。[5]例如:
template<class T> class A { /* ... */ };
template<class T, class U = T> class B { /* ... */ };
template <class ... Types> class C { /* ... */ };
template<template<class> class P> class X { /* ... */ };
template<template<class ...> class Q> class Y { /* ... */ };
X<A> xa; // OK
X<B> xb; // ill-formed: default arguments for the parameters of a template argument are ignored
X<C> xc; // ill-formed: a template parameter pack does not match a template parameter
Y<A> ya; // OK
Y<B> yb; // OK
Y<C> yc; // OK  
template <class T> struct eval;
template <template <class, class...> class TT, class T1, class... Rest>
struct eval<TT<T1, Rest...>> { };
template <class T1> struct A;
template <class T1, class T2> struct B;
template <int N> struct C;
template <class T1, int N> struct D;
template <class T1, class T2, int N = 17> struct E;
eval<A<int>> eA; // OK: matches partial specialization of eval

參考文獻

編輯
  1. ^ (https://stackoverflow.com/questions/63280883/is-the-code-below-using-template-template-parameters#comment111899635_63280883)
  2. ^ C++11語言標準,§14.1.14: A template-parameter of a template template-parameter is permitted to have a default template-argument.When such default arguments are specified, they apply to the template template-parameter in the scope of the template template-parameter.
  3. ^ C++11語言標準,§14.3.3.1: When the template-argument names a class template, only primary class templates are considered when matching the template template argument with the corresponding parameter; partial specializations are not considered even if their parameter lists match that of the template template parameter.
  4. ^ C++11語言標準,§14.3.3.2: If a specialization is not visible at the point of instantiation, and it would have been selected had it been visible, the program is ill-formed; no diagnostic is required.
  5. ^ C++11語言標準,§14.3.3.3: When P’s template-parameter-list contains a template parameter pack (14.5.3), the template parameter pack will match zero or more template parameters or template parameter packs in the template-parameter-list of A with the same type and form as the template parameter pack in P (ignoring whether those template parameters are template parameter packs).