zoukankan      html  css  js  c++  java
  • 第5课 模板的细节改进(2)_模板模板参数

    1. 模板模板参数 (Template template parameter)

    (1)模板模板参数:即模板的参数也是一个模板

    (2)声明格式形如:

    template<typename T, template<typename U> class Container> //参数Container本身是一个模板,其参数为U类型。
    class XCLS
    {
    private:
        Container<T> c; 
    };

    2. 模板模板参数的实参匹配

    (1)实参与Container模板的参数必须完全匹配

    template<typename T>
    using Lst = std::list<T, std::allocator<T>>;
    
    XCLS<string, Lst> mylist; //这里的实参不能传入list,因为list模板有两个参数,与Container模板(只有一个参数)不匹配,可以用using来重定义来解决该问题。

    (2)当不需要使用模板参数时,参数名可以省略不写。如

       template<typename T2, template<typename> class CONT2> //注意,第2个typname后面没有参数名

    【编程实验】用不同容器实现的栈

     //test1.cpp

    #include <iostream>
    #include <deque>
    #include <vector>
    
    //1. 不使用using的模板模板参数(注意:第2个参数的类型是模板,而不是一般的类型!!!)
    //   由于第2个参数CONT是一个容器类的模板,这个模板本身要求两个参数ELEM和ALLOC。所以
    //   必须在第2个参数CONT(模板模板参数,也用template声明)中完整的写出,如下:
    template<typename T, template<typename ELEM, typename ALLOC = std::allocator<ELEM> > 
                         class CONT = std::deque>
    class Stack
    {
    private:
        CONT<T> elems;  //CONT<T, allocator<T> >
                        //deque<T, allocator<T> >
    public:
        void push(const T& x){elems.push_back(x);}
        void pop(){elems.pop_back();}
        
        T& top(){return elems.back();}
        bool empty() const {return elems.empty();}
        
    
        //缺省赋值运算符要求=两边具有相同的元素类型。通过定义一个模板形式的赋值运算符,
        //元素类型不同的两个栈就可实现相互赋值,如:
        //Stack<int> is1, is2;
        //Stack<double> ds3;
        //...
        //is1 = is2;  // OK: 具有相同类型的栈 
        //is3 = is1;  // ERROR:两边栈的类型不同    
        template<typename T2, template<typename ELEM2, typename ALLOC = std::allocator<ELEM2> > 
                        class CONT2>
        Stack<T, CONT>& operator=(const Stack<T2, CONT2>& rhs)
        {
            if((void*)this == (void*)&rhs)
                return *this;  //避免自赋值
            
            Stack<T2, CONT2> tmp(rhs);
            elems.clear();
            
            while(!tmp.empty())
            {
                elems.push_front(tmp.top());
                tmp.pop();
            }
            
            return *this;
        }
    };
    
    //2. 使用using优化模板模板参数(注意:第2个参数的类型是模板,而不是一般的类型!!!)
    template<typename T>
    using vec = std::vector<T, std::allocator<T>>;
    
    template<typename T>
    using deq = std::vector<T, std::allocator<T>>;
    
    template<typename T, template<typename> class CONT>
    class Stack2
    {
    private:
        CONT<T> elems;  //CONT<T, allocator<T> >
                        //deque<T, allocator<T> >
    public:
        void push(const T& x){elems.push_back(x);}
        void pop(){elems.pop_back();}
        
        T& top(){return elems.back();}
        bool empty() const {return elems.empty();}
        
    
        //缺省赋值运算符要求=两边具有相同的元素类型。通过定义一个模板形式的赋值运算符,
        //元素类型不同的两个栈就可实现相互赋值    
        template<typename T2, template<typename> class CONT2>
        Stack2<T, CONT>& operator=(const Stack2<T2, CONT2>& rhs)
        {
            if((void*)this == (void*)&rhs)
                return *this;  //避免自赋值
            
            Stack2<T2, CONT2> tmp(rhs);
            elems.clear();
            
            while(!tmp.empty())
            {
                elems.push_front(tmp.top());
                tmp.pop();
            }
            
            return *this;
        }
    };
    
    int main()
    {
        Stack2<int, vec> stack1; //注意,第2个参数为模板
        for(int i=0; i<10;i++){
            stack1.push(i);
        }
        
        Stack2<double, vec> stack2;
        stack2 = stack1;
        
        while(!stack2.empty()){
            std::cout << stack2.top() << std::endl;
            stack2.pop();
        }
        return 0;
    }

    3. 模板模板参数+using的妙用

    (1)实验要求:写一个能测试各种容器效率的函数或类,传入的参数为类型!

    (2)程序的进化

      ①最初的想法:利用普通函数来实现(天方夜谭)

    //最原始的想法:利用普通函数实现
    void test_moveable(Container cntr, T elem)
    {
        //传入的是对象cntr,却要拿它的类型做文章。(天方夜谭:无法获得Container类型!)
        Container<T> c;
        
        //插入大量的元素
        for(long i=0; i<SIZE; ++i)
        {
            c.insert(c.end(), T()); //同样elem是对象,无法获得T类型
        }
        
        output_static_data(T()); //打印T的静态数据
        //测试大量元素的拷贝构造
        Container<T> c1(c); 
        ...
        
        //测试大量元素的移动拷贝构造
        Container<T> c2(std::move(c));
        ...
        
        c1.swap(c2);
    }
    
    //函数调用:接口很漂亮,但无法实现这个目的。因为函数只能传入
    //对象或变量,而不能传入类型!
    test_moveable(list, MyString); //error,不能传入类型!
    test_moveable(list, MyStrNoMove); 

      ②改进的做法:利用函数模板实现(天方夜谭)

    //进化的做法:利用函数模板实现
    template<typename Container, typename T>
    void test_moveable(Container cntr, T elem)
    {
        //由于Container是个普通的模板参数,而不是模板模板参数。因此
        //不能像下列方式被使用!
        Container<T> c;  //typename Container<T> c; 也不行!
        
        //插入大量的元素
        for(long i=0; i<SIZE; ++i)
        {
            c.insert(c.end(), T()); 
        }
        
        output_static_data(T());
    
        Container<T> c1(c); 
        Container<T> c2(std::move(c));
    
        c1.swap(c2);
    }
    //函数调用:注意传入的是对象。list()为演示之用,实际上无法这样产生对象。
    test_moveable(list(), MyString()); //error,不能传入类型!
    test_moveable(list(), MyStrNoMove()); 

      ③不完美方案:利用迭代器与萃取机

    //不完美的进化:利用迭代器与萃取机(可以解决问题,但不完美!因为用到了stl中的迭代器,
    //这也限制了该函数只能用来测试stl标准库中的容器类)
    template<typename Container>
    void test_moveable(Container c)
    {
        //由于STL库中的迭代器都包含了元素的类型,可以使用traits萃取出来
        typedef typename iterator_traits<typename Container::iterator>::value_type Valtype;
        
        //插入大量的元素
        for(long i=0; i<SIZE; ++i)
        {
            c.insert(c.end(), Valtype()); //利用元素类型产生对象!
        }
        
        output_static_data(*(c.begin());//注意: output_static_data(const T& obj)
    
        Container<T> c1(c); 
        Container<T> c2(std::move(c));
    
        c1.swap(c2);
    }
    
    //函数调用:不完美,函数的接口变了。我们希望的是接入类型,而不是对象!
    test_moveable(list<MyString>()); 
    test_moveable(list<MyStrNoMove>());
    test_moveable(vector<MyString>()); 
    test_moveable(vector<MyStrNoMove>()); 

      ④终极方案:利用模板模板参数与模板别名

    //完美方案(利用模板模板参数+模板别名)
    //(Container参数是一个模板!注意其声明部分)
    template<typename T, template<typename> class Container> 
    class XCls
    {
        Container<T> c; //注意,Container本身是一个模板,只有一个参数!
                        //而stl中容器类有两个参数(如vector<T, allocator<T>>),
                        //会出现参数不匹配!如何解决:见调用时using的使用!
                        
    public:
        XCls(){
            for(long i=0; i<SIZE; ++i)
            {
                c.insert(c.end(), T()); 
            }
        
            output_static_data(T());
    
            Container<T> c1(c); 
            Container<T> c2(std::move(c));
    
            c1.swap(c2);        
        }    
    }; 
    
    //注意,template不能定义在函数内部!
    template <typename T>
    using vec = std::vector<T, std::allocator<T>>;
    
    template <typename T>
    using lst = std::list<T, std::allocator<T>>;
    
    //调用(理想的接口:传入类型!)
    XCls<MyString, vec> c1; //不能写成XCls<MyString, vector> cl,因为Container与
                               //vector的模板的参数不匹配。
    XCls<MyString, lst> c2;                    
  • 相关阅读:
    C语言博客作业06--结构体&文件
    C语言博客05--指针
    C语言博客作业04--数组
    LeetCode错题集
    C博客作业--指针
    深入浅出强化学习:原理入门(待更新)
    Detectron2环境配置+Ubantu+CUDA10.1+pytorch1.7.0
    论文记载:A Survey on Traffic Signal Control Methods
    论文记载:FRAP:Learning Phase Competition for Traffic Signal Control
    周博磊老师强化学习纲领笔记第三课:无模型的价值函数估计和控制
  • 原文地址:https://www.cnblogs.com/5iedu/p/7625355.html
Copyright © 2011-2022 走看看