template-template-paramenter


模版参数中包含模版

  • 允许模板参数本身也是一个类模板,这可能会很有用。我们将继续以stack类模板作为例子
template <class T1, template<class T2> 
class Container>

class XCls{

    private:
    Container<T> c;
    public:
    ...
}
template<class T>
using Lst = List<T,allocator<T>>;


XCls<string, Lst> mylst;

由于Stack类模板的声明中第二个参数是一个类型(typename Container),所以我们通过Stack<int, std::deque>定义一个具体的栈类型时,第二个参数传递std::deque,而不能是std::deque。上述定义中我们一共把int写了两遍,而这种重复是一种必然的重复。

为了避免上述重复,我们可以让Stack的第二个参数直接是一个模板,而不再是一个具体类型。

template<typename T,
         template<typename> class Container = std::vector>
struct Stack
{
    void push(const T& elem)
    {
        elems.push_back(elem);
    }

    T pop()
    {
        if(empty()) throw std::out_of_range("Stack<>::pop: empty!");

        auto elem = elems.back();
        elems.pop_back();
        return elem;
    }

    bool empty() const
    {
        return elems.empty();
    }

private:
    Container<T> elems;
};

如上Stack类模板的第二个参数变为template class Container,它的名字仍旧是Container,但是类型变为一个模板,这个模板具有一个类型参数。由于Container自身的模板形参名字没有被使用,所以我们可以省略。按照标准这里声明Container前的关键字只能是class,不能是typename。最后,模板的模板参数也可以有默认值,这里我们设置为std::vector。

有了上面的定义,我们期望可以这样使用Stack:Stack<int, std::deque> intStack,但编译器却给了我们一个教训。

std::deque类模板在stl库中的定义有两个类型参数,第一个参数是元素类型,第二个参数是分配器allocator的类型。虽然std::deque的第二个类型参数有默认值,但是当编译器使用std::deque替换Container时却会严格匹配参数,默认值被忽略了。

我们修改Stack的定义如下:

template<typename T,
         template<typename Elem, typename Allocator = std::allocator<Elem>> class Container = std::vector>
struct Stack
{
    void push(const T& elem)
    {
        elems.push_back(elem);
    }

    T pop()
    {
        if(empty()) throw std::out_of_range("Stack<>::pop: empty!");

        auto elem = elems.back();
        elems.pop_back();
        return elem;
    }

    bool empty() const
    {
        return elems.empty();
    }

private:
    Container<T> elems;
};

Author: Moule Lin
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source Moule Lin !
  TOC