C/C++编程:模板的模板参数

1059 篇文章 280 订阅

有时,让模板参数本身成为模板是很有用的。下面我们以stack类作为例子,来说明模板的模板参数的问题

在stack的例子中,如果要使用一个和缺省值不同的内部容器,程序员必须两次指定元素类型。也就是说,为了指定内部容器的类型,你需要同时传递容器的类型和它所含元素的类型。如下:

Stack<int, std::vector<int>> vStack; // 使用vector的int栈

然而,借助于模板的模板参数,你可以只指定容器的类型而不需要指定所含元素的类型,就可以声明这个Stack类模板:

Stack<int, std::vector> vStack; // 使用vector的int栈

为了获得这个特性,必须把第2个模板参数指定为模板的模板参数,那么,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 top() const;         // return top element
    bool empty() const {   // return whether the stack is empty
        return elems.empty();
    }
};

不同之处在于,第二个模板参数现在被声明为一个类模板:

template<typename ELEM> class CONT;

缺省值也从std::deque<T>变成了std::deque。在使用时,第二个参数必须是一个类模板,并且由第一个模板参数传递进来的类型实例化:

CONT<T> elems;

可以使用类模板内部的任何类型来实例化模板的模板参数

一般来说,可以使用typename来替换class。然而,上面的CONT是为了定义一个类,因此只能使用class。因此,下面的程序是正确的:

template <typename T,
          template <typename ELEM> class CONT = std::deque >
class Stack {

而下面的程序是错误的:

template <typename T,
          template <typename ELEM> typename CONT = std::deque >
class Stack {

由于在这里我们并不会用到模板的模板参数的模板参数(也就是上面的ELEM),所以可以省略不写:

template <typename T,
          template <typename> class CONT = std::deque >
class Stack {

另外,还必须对成员函数的声明进行相应的修改,你必须把第二个模板参数指定为模板的模板参数;这同样适用于成员函数的实现,例如:

template<typename T, template<typename>class CONT>
vois Stack<T, CONT>::push(T const & elem){
	elems.push_back(elem);
}

另外:函数模板并不支持模板的模板参数

完整的例子:

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 top() const;         // return top element
    bool empty() const {   // return whether the stack is empty
        return elems.empty();
    }
};
template <typename T, template <typename> class CONT>
void Stack<T,CONT>::push (T const& elem)
{
    elems.push_back(elem);    // append copy of passed elem
}

template<typename T, template <typename> class CONT>
void Stack<T,CONT>::pop ()
{
    if (elems.empty()) {
        throw std::out_of_range("Stack<>::pop(): empty stack");
    }
    elems.pop_back();         // remove last element
}

template <typename T, template <typename> class CONT>
T Stack<T,CONT>::top () const
{
    if (elems.empty()) {
        throw std::out_of_range("Stack<>::top(): empty stack");
    }
    return elems.back();      // return copy of last element
}

int  main()
{
    try {
        Stack<int>   intStack;       // stack of ints
        Stack<float> floatStack;     // stack of floats

        // manipulate int stack
        intStack.push(42);
        intStack.push(7);

        // manipulate float stack
        floatStack.push(7.7);

        // print float stack
        std::cout << floatStack.top() << std::endl;
        floatStack.pop();
        std::cout << floatStack.top() << std::endl;
        floatStack.pop();
        std::cout << floatStack.top() << std::endl;
        floatStack.pop();
    }
    catch (std::exception const& ex) {
        std::cerr << "Exception: " << ex.what() << std::endl;
    }

    // stack for ints using a vector as an internal container
    Stack<int,std::vector> vStack;
    //...
    vStack.push(42);
    vStack.push(7);
    std::cout << vStack.top() << std::endl;
    vStack.pop();
}

模板的模板参数匹配

如果你尝试使用新版本的Stack,你会获得一个错误信息:缺省值std::deque和模板的模板参数CONT不匹配。问题在于:模板的模板实参(这里是std::deque)是一个具有参数A的模板,它将替换模板的模板参数(这里是CONT),而模板的模板参数是一个具有参数B的模板:匹配过程要求参数A和参数B必须完全匹配,然而在这里,我们并没有考虑模板的模板实参的缺省模板参数,从而也就使B中缺少了这些缺省参数值,当然就不能获取精确的匹配

在这里,问题在于标准库的std::deque模板还具有另一个参数:即第2个参数(也就是内存分配器allocator),它有一个缺省值,但是在匹配std::deque的参数和CONT的参数时,我们并没有考虑这个缺省值。

解决方法:重写类的声明,让CONT的参数期望是具有两个模板参数的容器:

template <typename T, 
			template<typename ELEM, 
				typename ALLOC = std::allocator<ELEM>>
				class CONT = std::deque>

class Stack{
	private :
		CONT<T>elem;
}

同样,可以省去ALLOC不写,因为没有用到它:

现在,Stack模板(包括为了能够在不同元素类型之间的栈之间相互赋值而定义的成员模板)的最终版本如下:

#ifndef STACK_HPP
#define STACK_HPP

#include <deque>
#include <stdexcept>
#include <memory>

template <typename T,
          template <typename ELEM, 
                    typename = std::allocator<ELEM> >
                    class CONT = std::deque>
class Stack {
  private:
    CONT<T> elems;         // 保存元素的容器

  public:
    void push(T const&);   //压入元素
    void pop();            //弹出元素
    T top() const;         //返回栈顶元素
    bool empty() const {   //返回栈是否为空
        return elems.empty();
    }

    // 使用元素类型为T2的栈对原栈赋值
    template<typename T2, 
             template<typename ELEM2, 
                      typename = std::allocator<ELEM2>
                     >class CONT2>
    Stack<T,CONT>& operator= (Stack<T2,CONT2> const&);
};

template <typename T, template <typename,typename> class CONT>
void Stack<T,CONT>::push (T const& elem)
{
    elems.push_back(elem);    //附加传入元素的拷贝
}

template<typename T, template <typename,typename> class CONT>
void Stack<T,CONT>::pop ()
{
    if (elems.empty()) {
        throw std::out_of_range("Stack<>::pop(): empty stack");
    }
    elems.pop_back();         // 删除末尾元素
}

template <typename T, template <typename,typename> class CONT>
T Stack<T,CONT>::top () const
{
    if (elems.empty()) {
        throw std::out_of_range("Stack<>::top(): empty stack");
    }
    return elems.back();      //返回末端元素的拷贝
}

template <typename T, template <typename,typename> class CONT>
 template <typename T2, template <typename,typename> class CONT2>
Stack<T,CONT>&
Stack<T,CONT>::operator= (Stack<T2,CONT2> const& op2)
{
    if ((void*)this == (void*)&op2) {    // 赋值给自己吗?
        return *this;
    }

    Stack<T2,CONT2> tmp(op2);        //创建一个赋值的拷贝

    elems.clear();                   // 删除现有元素
    while (!tmp.empty()) {           //拷贝所有元素
        elems.push_front(tmp.top());
        tmp.pop();
    }
    return *this;
}

#endif // STACK_HPP

使用:

int  main()
{
    try {
        Stack<int> intStack;
        Stack<float> floatStack;

        intStack.push(42);
        intStack.push(7);

        floatStack.push(7.77);

        floatStack = intStack;

        std::cout << floatStack.top() << "\n";
        floatStack.pop();
        std::cout << floatStack.top() << "\n";
        floatStack.pop();
        std::cout << floatStack.top() << "\n";
        floatStack.pop();
    } catch (std::exception const  & ex) {
        std::cerr << "Exception:" << ex.what() << "\n";
    }

    Stack<int, std::vector> vStack;
    vStack.push(42);
    vStack.push(7);
    std::cout << "vector: " <<vStack.top() << "\n";
}

在这里插入图片描述

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值