本章节主要关于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];
}