C++ Template 基础篇(三):参数魔法

Template 基础篇-参数魔法

Template所代表的泛型编程是C++语言中的重要的组成部分,我将通过几篇blog对这半年以来的学习做一个系统的总结,本文是基础篇的第三部分。

  • Template 基础篇-参数魔法
    • 默认实参
    • 模板的模板参数
    • 非类型参数

除了使用类型作为模板的参数之外,模板参数有更多的用法,以下做个详细介绍。

默认实参

我们可以通过给模板指定默认实参,为用户推荐合适的默认设定,让用户在只指定部分(或完全不指定)实参的情况下使用模板。

注意:模板的默认实参与函数的默认实参一样,必须从右向左定义。

在C++98中,只能为类模板指定默认实参。

template<typename T = int, typename U> //error, 必须从右向左
struct Wrapper1 {
      T t;
};

template<typename T, typename U = int> //ok
struct Wrapper2 {
    T t;
};
Wrapper2<double> w;

template<typename T = int>
struct Wrapper3 {
    T t;
};
Wrapper3<> w; //w的类型为Wrapper,注意空的<>必须写上

在C++11中,还可以为函数模板指定默认实参

template<typename T, typename F = std::less> //指定F的默认值是std::less
int compare(const T& left, const T& right, F f = F()) { //注意这里,std::less是一个functor
    if (f(left, right)) {
        return -1;
    }
    if (f(right, left)) {
        return 1;
    }
    return 0;
}
compare(1, 2); //未指定第三个参数,使用默认值std::less

模板的模板参数

除了内置类型(int float bool等)和自定义类型(class struct)可以作为模板实参之外,C++还允许使用一个模板作为另外一个模板的实参,这使得模板的用户可以对模板的行为进行深度的定制。例如:指定模板存储数据使用另外一种STL容器。

声明模板的模板参数时,必须使用template和class关键字,必须使用另外一个模板的完整声明。

template<typename T, 
         template<typename ELEM> typename CONT> //error,必须使用class关键字
class ContainerWrapper {
public:
    CONT elems;
};

template<typename T, 
         template<typename ELEM> class CONT> //ok
class ContainerWrapper {
public:
    CONT elems;
};

ContainerWrapper<int, std::deque> cw; //error,std::deque不匹配
//std::deque中除了ELEM之外还有一个有默认值的Alloc参数

template<typename T, 
         template<typename ELEM, typename ALLOC = std::allocator> 
         class CONT = std::deque> //兼容STL容器的模板实参,默认为deque,注意template和class两个关键词
class ContainerWrapper {
public:
    CONT elems;
};

ContainerWrapper<int> cw_default; //使用deque存储
ContainerWrapper<double, std::vector> cw_customized; //指定底层存储类型为vector

非类型参数

C++模板还支持非类型参数,我们可以在模板定义中使用一个具体类型来指定它们。一个非类型参数可以是一个整形,或者是指针或左值引用。

template<unsigned N, unsigned M> //使用unsigned标记非类型参数
int compare(const char (&left) [N], const char (&right) [M]) {
    return compare(left, right);
}

compare("hi", "hello"); //推断出N=3,M=6

template<typename T, int N>
class WrapperWithNum {
public:
    T t;
};

WrapperWithNum<int, 1> wwn1;
WrapperWithNum<int, 2> wwn2; //注意:虽然两个模板实例的T都是int,但是因为N不同,所以这是两个类型

注意:对于含有非类型参数的函数模板,整形推断的结果必须是一个常量表达式,指针和引用推断的结果必须指向具有静态生存周期的对象(static或者全局)或者是nullptr或0。

你可能感兴趣的:(c++)