读书笔记

decltype的关注点

两种场景

decltype有两种推导场景: 标识符的类型和表达式的类型. 区别是带不带括号. 如果不带括号, 表示要推导标识符的类型. 如果带了括号, 表示要推导表达式的类型. 我们通常看得不仔细, 往往会忽略后面一点.

当要推导表达式时, 遵循下面的规则:

  • 若表达式的值为左值, 则推导出T&
  • 若表达式的值为纯右值, 则推导出T
  • 若表达式的值类别为将亡值, 则推导出T&&

例如, 考虑下面的声明:

1
2
3
4
5
QPoint pt;
QPoint* pPt = &pt;
const QPoint* cpPt = & pt;
QPoint& lrPt = pt;
QPoint&& rrPt = {};

则表达式推导结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 左值
using T1 = decltype((pt)); // T1为QPoint&
using T2 = decltype((pPt)); // T2为QPoint*
using T3 = decltype((pt.x)); // int&
using T4 = decltype((++pt.x)); // int&
// 纯右值
using T11 = decltype((pt.x++)); // int
using T12 = decltype((QPoint(1,1))); // QPoint
using T13 = decltype((5)); // int
// 将亡值:
using T21 = decltype((QPoint(10,10).x)) // int&&
using T22 = decltype((std::move(pt))); // QPoint&&
using T23 = decltype((static_cast<QPoint&&>(pt))); // QPoint&&

decltype(auto)的处理

在这里面, auto是一个占位符, 代表了等号右边的表达式. 因此, 我们可以按照这个规则进行理解, 同样存在标识符和表达式的两种场景.
从这个角度看, 它类似于std::forward, 可以保留值语义和引用语义.

变量的推导:

1
2
decltype(auto) v1 = pt;     // QPoint
decltype(auto) v1 = (pt); // QPoint&

函数返回值的推导:

1
2
3
4
5
6
7
string fun1();
string& fun1();

decltype(auto) test_1() { return fun1(); } // QPoint
decltype(auto) test_2() { return fun2(); } // QPoint&
decltype(auto) test_3() { auto str = fun1(); return str; } // QPoint
decltype(auto) test_4() { auto str = fun2(); return (str); } // 有问题, 悬挂引用了.

注意test_4(), 它返回的是一个局部变量的引用!