《Effective Modern C++》条款3:理解decltype

本章节主要关于decltype进行讨论,仍然在条款一和条款二的基础上;

一、基本用途:

目前综合章节来看,decltype关键字最主要的用途师姐和函数返回值auto进行模板型别推导,如果单独使用可以推导出某个变量的具体类型;

1.辅助auto进行返回类型推断:

目前主要用于辅助auto,考虑下列代码:

template<typename Container,typename Index>
auto authAndAccess(Container& c, Index i) {
    return c[i];
}

乍一看可能没有什么问题,但是如果执行下述操作:

vector<int>vec = { 1,2,3 };
authAndAccess(vec, 2) = 6;

在这里插入图片描述
原因出在auto和[]操作符问题上;

对于有些容器来说,operator[]一般返回两种:T或者T&;

对于上述例子,[]返回的应该是引用,但是由于auto作返回型别的时候只会被认为是拷贝返回,也就是条款1的第三种情况;

因此返回的实际上是一个int值,而非int&,因此向int值右值赋值是错误的行为;

因此,如果结合decltype,会达到预期的效果:

template<typename Container,typename Index>
auto authAndAccess(Container& c, Index i) ->decltype(c[i]){
    return c[i];
}

通过“返回值型别尾序语法”可以很好的去构建返回值为我们期望类型的型别,通过decltype来使得返回值型别和c[i]返回的型别一致,保证总是同步;

值得注意的是,通过decltype,可以弥补很多简单使用auto的场景下的缺陷;

之前条款2讲过,auto类似于模板型别推导中简单的使用T,相当于条款一中的第三种情况,完全拷贝型别推导;

但是如果加入decltype后,可以让auto推导出来的值随着传参型别同步变化;

vector<int>v(3, 0);
const vector<int>& cw = v;
auto a1 = cw;//此时a1为vector<int>,拷贝模板推导忽略const和引用性质;
decltype(auto) a2 = cw;//和cw类型同步,都为cosnt vector<int>&;

从上述可以看出,decltype让auto更加的“自适应”,不用像模板型别推导那样,需要注意引用和非引用情况;

对于C++14来说,可以直接支持返回值为decltype(auto)进行推导,而C++11只能使用尾序推导进行推导;

c11:
template<typename Container,typename Index>
auto authAndAccess(Container& c, Index i) ->decltype(c[i]){
    return c[i];
}
c14:
template<typename Container,typename Index>
decltype(auto) authAndAccess(Container& c, Index i){
    return c[i];
}

2.对于表达式场景:

这里注意一下表达式的推断,对于复杂表达式,decltype会推断为T&,而非T,这里要注意下:

decltype(auto) fun() {
    int x = 3;
    return (x);
}

decltype(auto) vec = fun();

此时vec为int&;

decltype(auto) fun() {
    int x = 3;
    return x;
}

decltype(auto) vec = fun();

此时vec为int;

所以要注意下,有可能因为一个表达式或者括号,引起不必要的错误;

函数体内的对象有可能会析构,如果返回引用使用可能会引起不必要的错误;

二、围绕版本的一些奇奇怪怪的改进:

对于上述描述,还是有一些不符合预期的场景出现:

template<typename Container,typename Index>
auto authAndAccess(Container& c, Index i) ->decltype(c[i]){
    return c[i];
}

对于该情况,Container的接收模板型别推导为引用,因此只能接收左值,无法接收右值;

考虑工厂函数场景:

vector<string> makevector();
auto i=authAndAccess(makevector(),5);

上述代码调用工厂函数,进行传参,希望在authAndAccess中对某个位置值进行修改处理;

但是必定会失败,原因是makevector产生一个右值,authAndAccess则期望接受一个左值;

对于该问题,我们其实可以通过条款1的第二种万能引用操作进行解决;

template<typename Container,typename Index>
auto authAndAccess(Container&& c, Index i) ->decltype(c[i]){
    return c[i];
}

对于右值操作,还需要搭配std::forward<Container>进行操作,forward具体用法后续条款再补;

因此对于c11和c14有最终版本:

c11:
template<typename Container,typename Index>
auto authAndAccess(Container&& c, Index i) ->decltype(std::forward<Container>(c)[i]){
    return std::forward<Container>(c)[i];
}
c14:
template<typename Container,typename Index>
decltype(auto) authAndAccess(Container& c, Index i){
     return std::forward<Container>(c)[i];
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值