decltype

decltype就是用于给定的名字或者表达式,decltype能告诉你该名字或表达式的型别。通常来说结果是符合自己的预期的。

const int i = 0; // decltype(i) 是 const int
 bool f(const Widget& w); //decltype(w) 是 const Widget&
 struct Point {
   int x, y;  // decltype(Point::x) 是int
 };
 Widget w;  // decltype(w) 是Widget
 if (f(w)) ... // decltype(w) 是 bool
 template<typename T>
 class Vector {
 public: 
   T& operator[] (std::size_t index);
 }; 
 Vector<int> v; // decltype(v) 是Vector<int>
 if (v[0] == 0) ; // decltype(v[0]) 是int&

一般来说, 含有型别T的对象容器, operator[] 会返回T&, 只有std::vector 不会返回bool&,而是一个全新的对象。

#include <iostream>
#include <deque>
template<typename Container, typename Index>
auto authAndAceesss(Container&c, Index i) -> decltype(c[i]) {
  return c[i];
}
int main(){
  std::deque<int> d;
  authAndAceesss(d, 5) = 10;
  printf("%d\n", d[5]);
  return 0;
}

编译运行

g++ -o tt  test_decltype.cpp -std=c++11
./tt

在c++11需要保留尾序返回值型别,在c++14只需要保留auto就行了。

// C++14 
template<typename Container, typename Index>
auto authAndAccess(Container& c, Index i) { // 不太正确
  // ....
  return c[i]; // 返回值根据c[i]推导出来
}

虽然编译器会对auto指定为返回型别的函数进行模板型别推导,而这个对大多数包含型别T的对象的容器operator[]会返回T&,但是
模板推导过程中,初始化表达式中的引用性会被忽略。
比如这段代码编译不通过

#include <iostream>
#include <deque>
template<typename Container, typename Index>
auto authAndAceesss(Container&c, Index i) {
  return c[i];
}
int main(){
  std::deque<int> d;
  authAndAceesss(d, 5) = 10;
  printf("%d\n", d[5]);
  return 0;
}

编译不过

test_decltype.cpp: In function ‘int main()’:
test_decltype.cpp:9:24: error: lvalue required as left operand of assignment
   authAndAceesss(d, 5) = 10;

d[5]返回的是int&, 但是authAndAccess的返回值实施型别推导,实际上是模板型别推导,对包含型别T将剥离引用饰词,返回值变成了int
这是一个右值,所以编译出错。

为了使得authAndAceess方法和我们预期的一样,就需要对返回值进行decltype型别推导,使得authAndAcess返回值与c[i]的返回型别完全一样。

// g++ -o tt  test_decltype.cpp -std=c++14
#include <iostream>
#include <deque>
template<typename Container, typename Index>
decltype(auto) authAndAceesss(Container&c, Index i) {
  return c[i];
}
int main(){
  std::deque<int> d;
  authAndAceesss(d, 5) = 10;
  printf("%d\n", d[5]);
  return 0;
}

c++14以上可以这样使用,这里看起来比较奇怪,有auto也有decltype.
其实应该这样理解:

  1. auto指定型别推导。
  2. decltype指定型别推导过程采用decltype的规则。
    当然在申明变量的时候也可以应用decltype型别推导
Widget w;
const Widget& cw = w;
auto myWidget1 = cw; // auto型别推导 myWidget的型别是Widget
decltype(auto) myWidget2 = cw; // decltype型别推导 myWidget的型别是const Widget&
这里有个问题

C++14版的authAndAccess

template<typename Container, typename Index>
decltype(auto) authAndAccess(Container& c, Index i);

容器传递的是非常量的左值引用(lvalue-reference-to-non-const),而该函数返回的是该容器的某个引用,就允许
用户通过authAndAccess对容器进行修改,但是这样就不能传递右值容器。

虽然向authAndAceess传递右值属于罕见情况,一般调用右值,会在authAndAccess语句结束处被析构,也就是该容器的某个元素的引用
会在改该语句结束的时候被置于空悬状态,但是向authAndAccess传递一个右值应该是合理的。
比如这样

std::deque<std::string> makeStringDeque(); // 工厂函数
// 制作makeStringDeque返回的deque的第五个元素的副本
auto s = authAndAccess(makeStringDeque(), 5);

为了能够支持这种语法,就得修改authAndAccess的声明,让它可以接受左值也能接受右值。
这时候就需要万能引用。

template<typename Container, typename Index>
decltype(auto) authAndAccess(Container&& c, Index i); // now c 就是万能引用。

那么对于万能引用,我们就应该使用std::forward

// c++14

template<typename Container, typename Index>
decltype(auto) authAndAceesss(Container&& c, Index i) {
  return std::forward<Container>(c)[i];
}
// c++11
template<typename Container, typename Index>
decltype(auto) authAndAceesss(Container&& c, Index i) -> decltype(std::forward<Container>(c)[i]) {
  return std::forward<Container>(c)[i];
}
注意下面这个case

如果将变量放到小括号中

decltype(auto) f1() {
 int x = 0;
 return x; // decltype(x) 是int, 所以f1返回是一个int
}
decltype(auto) f2() {
 int x = 0;
 return (x); // decltype((x))是一个int&, 所以返回的是一个int&
}

f2返回了一个局部变量的引用,就会出现未定义错误。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值