c++11-17 模板核心知识(十二)—— 模板的模板参数 Template Template Parameters

概念

一个模板的参数是模板类型。c++

举例

c++11-17 模板核心知识(二)—— 类模板 中,若是咱们想要容许指定存储Stack元素的容器,是这么作的:git

template <typename T, typename Cont = std::vector<T>> 
class Stack {
private:
  Cont elems; // elements
  ......
};

使用:github

Stack<double,std::deque<double>> dblStack;

可是这样的缺点是须要指定元素类型两次,然而这两个类型是同样的。app

使用模板的模板参数(Template Template Parameters),容许咱们在声明Stack类模板的时候只指定容器的类型而不去指定容器中
元素的类型。例如:函数

template <typename T, template <typename Elem> class Cont = std::deque>
class Stack {
private:
  Cont<T> elems; // elements
public:
  void push(T const &); // push element
  void pop();           // pop element
  T const &top() const; // return top element
  bool empty() const {  // return whether the stack is empty
    return elems.empty();
  }
  ...
};

使用:c++11

Stack<int, std::vector> vStack;      // integer stack that uses a vector

与第一种方式的区别是:第二个模板参数是一个类模板:code

template<typename Elem> class Cont

默认值从std::deque<T>改成了std::deque.element

在C++17以后,模板的模板参数中的class也可使用typename,可是不可使用struct和union:get

template <typename T,
          template <typename Elem> typename Cont = std::deque>
class Stack {       // ERROR before C++17
  ...
};

......

template<template<typename X> class C> // OK
void f(C<int>* p);

template<template<typename X> struct C> // ERROR: struct not valid here
void f(C<int>* p);

template<template<typename X> union C> // ERROR: union not valid here
void f(C<int>* p);

固然,因为模板的模板参数中的Elem没有用到,能够省略:编译器

template <typename T, template <typename> class Cont = std::deque> 
class Stack {
  ...
};

另外注意一点,模板的模板参数中的模板参数,只能和模板的模板参数配合用。有点饶,举个例子:

template<template<typename T, T*> class Buf>        // OK
class Lexer {
    static T* storage;        // ERROR: a template template parameter cannot be used here
    ...
};

模板的模板参数的参数匹配 Template Template Argument Matching

你们能够尝试本身编译一下上面的代码,可能会出现下列问题:

error: template template argument has different template parameters than its corresponding template template parameter
template <typename T, template <typename Elem> class Cont = std::deque>

...

/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/deque:1197:1: note: too many template parameters in template template argument
template <class _Tp, class _Allocator /*= allocator<_Tp>*/>

意思是std::dequeCont不匹配。标准库的std::deque有两个参数,还有一个默认参数Allocator :

template <class _Tp, class _Allocator = allocator<_Tp> > class _LIBCPP_TEMPLATE_VIS deque;

解决办法一

将Cont和std::deque的参数匹配便可:

template <typename T,
          template <typename Elem, typename Alloc = std::allocator<Elem>>
          class Cont = std::deque>
class Stack {
......
};

这里的Alloc没有用到,一样能够省略。

成员函数定义举例:

template<typename T, template<typename,typename> class Cont>
void Stack<T,Cont>::push (T const& elem) {
    elems.push_back(elem);       // append copy of passed elem
}

解决办法二

利用c++11-17 模板核心知识(四)—— 可变参数模板 Variadic Template

template <typename T,
          template <typename......>
          class Cont = std::deque>
class Stack {
......
};

可是,这点对于std::array无效,由于std::array的第二个参数是非类型模板参数 Nontype Template Parameters:

// template<typename T, size_t N>
// class array;

假如使用 Stack<int,std::array> s;,那么编译器会报错:

/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/array:126:29: note: template parameter has a different kind in template argument
template <class _Tp, size_t _Size>
                            ^
main.cc:22:33: note: previous template template parameter is here
          template <typename... Elem>
                                ^

(完)

朋友们能够关注下个人公众号,得到最及时的更新:

image