在C++模板中,设置一个无名模板参数的默认值为0到底是什么含义

看下面的例子

#include<type_traits>
// define a class template, and the template parameter is a value of bool。by default,this class is empty, doesn't define any member
template<bool Condition>
struct lfEnableIf
{ };
// specialize the above class template, only when the value is true, the class has a defined type member--Type
template<>
struct lfEnableIf<true>
{
   typedef int Type;
};

// define a class template which has a static constant value, true/false
template<bool Val>
struct lfBoolConstant
{
    static const bool Value = Val;    
};

//定义两个特化后的模板类,其中一个具有静态常量 value=true, 另一个具有静态常量value=false
typedef lfBoolConstant<true> lfTrueType;
typedef lfBoolConstant<false> lfFalseType;

//定义一个模板类lfIsArithmetic,默认含有一个静态常量value=false
template <typename NumT> struct lfIsArithmetic : lfFalseType{};

// 特化上面的模板类,使其在参数类型为char,bool,float时,特化模板类含有一个静态常量value=true
template <> struct lfIsArithmetic<char> : lfTrueType{};
template <> struct lfIsArithmetic<bool> : lfTrueType{};
template <> struct lfIsArithmetic<float> : lfTrueType{};

// 最后的应用,使用上述定义的各个模板类,定义一个新的模板类,这个新的模板类含有两个模板参数,其中第一个是个类型参数,第二个是个数值型参数
template<typename T, typename lfEnableIf<lfIsArithmetic<T>::Value>::Type = 0>  // v or no v is same,nameless parameter SFINAE
struct Test
{
    static void print()
    {
        std::cout << "参数类型为:" <<typeid(T).name()<< std::endl;
    }

};

int main()
{
    Test<float>::print();// template<float, float = 0> struct Test;
    Test<bool>::print();// template<bool, bool = 0> struct Test;
    Test<char>::print();// template<char, char = 0> struct Test;
    // Test<int>::print();       // error, as fEnableIf<lfIsArithmetic<int>::Value>::Type doesn't exist at all

    return 0;
}

执行结果为:

让我们来看这一行,template<typename T, typename lfEnableIf<lfIsArithmetic<T>::Value>::Type = 0> struct Test的第二个模板参数,设置一个无名的模板参数的默认值为0到底是什么含义?

这一行其实定义一个有名的模板类型参数T和一个无名的模板数值型参数lfEnableIf<lfIsArithmetic<T>::Value>::Type,这第二个参数等价于template<int n=0> struct S{};

在lfEnableIf条件满足(lfIsArithmetic<T>::Value=true)的情况下,类型 lfEnableIf<lfIsArithmetic<T>::Value>::Type最终会被解析为类型int,于是上述那一堆就等价于

template<float, int = 0> struct Test;

template<bool, int = 0> struct Test;

template<char, int = 0> struct Test;

由于第二个模板参数的类型是一个在lfEnableIf范围内定义的一个内嵌类型,因此前面必须有typename 来告诉编译器,lfEnableIf<lfIsArithmetic<T>::Value>::Type是一个类型,而不是一个其它什么东西(比如不是类中的静态变量什么的)。同时这第二个模板参数是无名的,但是你可以给这个参数定义任何名字,这不会改变任何东西。比如template<typename T, typename lfEnableIf<lfIsArithmetic<T>::Value>::Type V = 0>

在上面的例子中,我给了这个无名参数一个名字V,这个参数名字在模板中任何地方都没有使用到,这正是我们没必要给一个确切名字的原因。这是一个虚参数,这也正是我们可以给这个虚参数赋予任何值的原因。我们可以设置默认值为0,也可以设置为42,这不会改变任何事情

于是在这种情况下,这两个关键字typename 就会带来一个误导:那就是你的这个模板中的第一个和第二个typename是类似的,其实不然,这两个typename 其实发挥的作用是完全不同的。第一个参数中的typename表示我定义一个模板参数类型T,这种情况下,typename 可以被class代替。而第二个typename, typename lfEnableIf<lfIsArithmetic<T>::Value>::Type = 0,则发挥一个不同的作用。typename仅仅用来告知编译器它后面的表达式是一个类型, 于是第二个参数就成为一个数值型模板参数(因为数值型模板参数不需要typename),这种情况下,typename不能被class替换

那么这个无名的模板参数默认值为0到底如何使用呢?那就是使用SFINAE原则,看typename lfEnableIf<lfIsArithmetic<T>::Value>::Type = 0能否被正确解析,如果能被正确解析,那么Test结构就存在,Test结构就可以被使用。

比如T使用char时,lfIsArithmetic<char>就含有value=true的成员变量。于是lfEnableIf<true>就含有一个int型的类型Type,于是typename lfEnableIf<lfIsArithmetic<T>::Value>::Type就等价为int,于是Test<char> 就存在,接可以被使用。

如果T为int时,由于lfIsArithmetic<int>只含有value=false的成员变量,于是lfEnableIf<fasle>就不会定义type成员变量,于是解析lfEnableIf<lfIsArithmetic<T>::Value>::Type就报错,于是Test<int> 就不存在,就不能使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值