C++11新特性(侯捷)——课程笔记(五)

这部分的内容是从一个实际问题出发,从alias template(模板别名)引申到template template parameter(模板模板参数)

一、Alias Template

一个模板别名的例子如下:

template <typename T>
using Vec = std::vector<T, MyAlloc<T>>;

//我们就可以这样用
Vec<int> coll;
//这就等同于
std::vector<int, MyAlloc<int>> coll;

所以模板别名是可以接受参数的。使用宏和typedef均达不到模板别名这种效果。typedef不能接受参数,所以使用typedef,最多达到下面这种效果:

typedef std::vector<int, MyAlloc<int>> Vec;

这样的定义缺乏灵活性,并且定义的Vec也不是一个模板;使用宏的话出来的效果也不符合我们的要求:

#define Vec<T> template <typename T> std::vector<T, MyAlloc<T>>;

//使用的时候Vec<int>就会直接变为:
template <typename int> std::vector<int, MyAlloc<int>>    //一个四不像

二、应用实例(引出模板模板参数)

考虑这样一种需求,假设我们需要实现一个函数test_moveable(容器对象,类型对象),从而能实现传入任意的容器和类型,都能将其组合为一个新的东西:容器<类型>,这样的话我们的函数应该怎么设计呢?

1. 解法一:函数模板(无法实现)

template <typename Container, typename T>
void test_moveable(Container cntr, T elem)
{
    Container<T> c;            //[Error] 'Container' is not a template
    
    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<int>的对象,那么Container就等同于list<int>,而list<int>并不是一个模板,我们希望改变list尖括号中的类型,但这样设计并不能做到。

2. 解法二:函数模板+iterator+traits(可以实现)

template<typename Container>
void test_moveable(Container c)
{
    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()));
    Container<T> c1(c);
    Container<T> c2(std::move(c));
    c1.swap(c2);
}

(注:这里还涉及到了typename的第二种用法,用于表示后面跟着的是一个类型,主要是为了避免歧义,因为作用域符号后面跟着的可能是类型,也可能是成员,具体见链接https://www.cnblogs.com/wuchanming/p/3765345.html)

这样做是可以达到效果的,但是却改变了函数签名,使用的时候我们需要这样调用:test_moveable(list<int>()),和我们开始设计的是不一样的。

那么,有没有template语法能够在模板接受一个template参数Container时,当Container本身又是一个class template,能取出Container的template参数?例如收到一个vector<string>,能够取出其元素类型string?那么这就引出了模板模板参数的概念。也就是下面的解法三。

3. 解法三:模板模板参数 + alias template(可以实现)

template <typename T,
          template <typename T>
              class Container
         >
class XCls
{
private:
    Container<T> c;
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);
    }
};

(注:模板模板参数中的T可以不写,默认就是前面的T)

使用上面的定义,在实际使用时还是会报错:

XCls<MyString, vector> c1;        //[Error] vector的实际类型和模板中的Container<T>类型不匹配

这是因为vector其实有两个模板参数,虽然第二个有默认值,我们平时也可以像vector<int>这样用。但是在模板中直接这样写是不匹配的。所以这里就用到了我们一开始提到的模板别名,只要传入的是vector的模板别名就可以了,如下所示:

//不得在function body之内声明
template<typename T>
using Vec = vector<T, allocator<T>>;

XCls<MyString, Vec> c1;

其中模板别名的定义不能在function body之内,也就是需要写在任何函数的外面,包括主函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值