C++11 模板元编程 - 模板的模板参数


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

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

template 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 elems;
};

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

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

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

我们修改Stack的定义如下:

template> 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 elems;
};

现在Stack intStack可以编译通过了!

可以看到模板的模板参数特性,可以让类模板之间通过模板参数互相组合。如果我们将类模板比作C++编译期的函数,那么可以接受模板作为参数的类模板,就相当于一个函数的入参仍旧可以是函数,这是后面我们会介绍到的高阶函数的概念。


模板的特化

返回 C++11模板元编程 - 目录

你可能感兴趣的:(C++11 模板元编程 - 模板的模板参数)