C++11 学习笔记 模板的细节(右尖括号,using,函数模板默认参数)

本文介绍了C++11中模板的三个改进:1) 解决了右尖括号导致的二义性问题,允许在模板参数中使用>>>;2) 引入using关键字创建模板别名,简化类型定义;3) 函数模板支持默认模板参数,提供更灵活的调用方式,并讨论了与模板参数推导的交互规则。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.模板的右尖括号

在C++98/03的泛型编程中,模板实例化有一个很繁琐的地方,就是连续两个右尖括号(>>)会被编译解释成右移操作符,而不是模板参数表的形式。即

<span style="font-size:14px;">template <typename T>
struct Foo
{
      typedef T type;
};

template <typename T>
class A
{
    // ....
};

int main(){
    Foo<A<int>>::type xx;//编译出错
    return 0;
}</span>
正确的写法是写成Foo<A<int> >xx。

在C++的成对括号中,只有右尖括号连续写两个会出现这二义性。当static_cast,reinterpret_cast所需转换的类型为模板时,用起来就很麻烦。在C++11中,这种限制被取消了。在C++11标准中,要求编译器对模板的右尖括号做单独处理,使编译器能够正确判断出">>"是一个右移操作符还是模板参数表的结束标记。

template <int N>
struct Foo
{
     //...
};

int main(){
     Foo<100 >> 2> xx;
     return 0;
}

上面的例子中:

1).C++98/03的编译器是没有问题的

2).C++11则会出现错误。解决方法:Foo< (100 >> 2) > xx;


二.模板的别名(using 的用法)

我们知道,typedef可以重定义一个类型的别名,原有类型的一个新的名字,如typedef int Integer;

当我们要定义一个固定以std::string为key的map,我们就需要将其写成如下的模板型式。

template <typename Val>
struct str_map
{
     typed std::map<std::string, Val> type;
};

str_map<int>::type ma;
需要加一个繁琐的str_map外敷类,这在我们使用泛型代码时就很麻烦。

现在C++11出现可以重定义一个模板的语法。

template <typename Val>
using str_map = std::map<std::string, Val>;

str_map<int> map1;

使用新的using别名语法定义了std::map的模板别名str_map_t。这相比于使用typedef,是不是简洁了很多呢。


事实上,using包含了typedef的全部功能:

1).对普通类型的重定义(两者等价):

typedef unsigned int unit_t;

using unit_t = unsigned int;

很明显,C++11的using别名语法比typedef清晰,简单。因为typedef的别名语法本质上类似一种解方法的思路。而using语法通过赋值来定义别名,和我们平时思考方式一致。

2).对模板的定义

template <typename T>
struct fun_t
{
     typedef void (*type)(T, T);
};

fun_t<int>::type xx_1;

template <typename T>
using func_t = void(*)(T, T);

func_t<int> xx_2;

使用using只是原有的基础上加上template参数列表就可,而不需要像typedef使用繁琐的外敷模板。


三.函数模板的默认模板参数

1.

在C++98/03中,类模板可以有默认参数,而函数模板不支持默认参数

//OK
template <typename T, typename U =int, U N = 0>
struct Foo
{
     //...
};

//error
template <typename T = int>
void func()
{
     //...
};
在C++11中,解除了这个限制。可以直接调用。如:

int main(){
     func();
     return 0;
}
函数模板的调用中,如果所有模板参数都有默认参数时,就可以像普通函数一样进行调用。

而在类模板中,哪怕所有参数都有默认参数,在使用时也必须在模板名后跟随"<>"来实例化

2.

函数模板的默认模板参数在使用规则上和其他的默认参数也有一些不同,它就没有写在参数表最后的限制。因此,当默认模板参数和模板参数自动推导结合起来使用时,就非常灵活。

template <typename R = int, typename U>
R fun(U val)
{
     val
}

int main(){
     fun(123);
     return 0;
}

易错点:当默认模板参数和模板参数自动推导同时使用时,若函数模板无法自动推导出参数类型时,则编译器将使用默认模板参数;否则使用自动推导出的参数。

template <typename T>
struct identity
{
     typedef T type;
}

template <typename T = int>
void func(typename identity<T>::type val, T = 0 )
{
     //...
}

int main(){
     func(123);
     func(123, 123.0);
     return 0;
}
func(123)中,默认类型起作用,val为int。

func(123, 123.0),由于第二个参数是double类型,模板参数T将优先被推导为double,因此,val为double。






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值