Item 3: Understand decltype

这次是对 Effective Modern C++ Item3 的学习笔记。

给定一个名字或者表达式,decltype 返回名字或者表达式的类型。大多数情况下,decltype 都能返回你所期望的结果,但也有一些特殊情况。

下面给出一些典型场景下,decltype 的返回结果,正如你所期望的那样:

const int i = 0; // decltype(i) is const intbool f(const Widget& w); // decltype(w) is const Widget&// decltype(f) is bool(const Widget&)struct Point {int x, y;   // decltype(Point::x) is int
};          // decltype(Point::y) is intWidget w; // decltype(w) is Widgetif (f(w)) ... // decltype(f(w)) is booltemplate<typename T>    // simplified version of std::vector
class vector {public:
...
T& operator[](std::size_t index);
...
};vector<int> v;  // decltype(v) is vector<int>
...
if (v[0] == 0) ...   // decltype(v[0]) is int&

在C++11中,可能 decltype 基本的应用就是申明函数模板,它的返回值类型取决于其参数类型。例如,我们有这样一个函数,传入一个容器和下标,期望在函数末尾返回下标指向的值,并且保留返回值的类型。由于容器的 operatpr[] 通常返回一个 T& (但是,std::vector<bool>,opertor[] 返回的不是一个 bool&,这个我们在 Item6 中再讨论),我们期望在函数末尾返回的也是引用类型,以便于我们对其进一步赋值。使用 decltype 达成这样的目标:

template<typename Container, typename Index>  // works, but requires refinement
auto authAndAccess(Container& c, Index i)
-> decltype(c[i])
{authenticateUser();return c[i];
}

这里使用了拖尾返回值类型的语法,也即在箭头后声明返回值类型,这里可以返回我们期望的引用类型。

C++14可以省略拖尾返回值的声明,这意味着 auto 需要通过编译器根据函数的实现进行类型推导得到:

template<typename Container, typename Index>   // C++14; not quite correct
auto authAndAccess(Container& c, Index i)
{authenticateUser();return c[i];    // return type deduced from c[i]
}

但是 Item1 和 Item2 的推导规则告诉我们,在模板类型推导中,表达式初始化的引用属性将被忽略。考虑下面的代码片段:

std::deque<int> d;
...
authAndAccess(d, 5) = 10;   // authenticate user, return d[5], then assign 10 to it; this won't compile!

这里,d[5] 是一个 int&,但是根据 auto的类型推导,authAndAccess 函数将会返回一个 int 类型,作为函数返回值,即一个 int 类型的值,这是一个右值,那么对其进行赋值是非法的。

为了得到我们期望的结果,也即返回引用类型,我们需要使用 decltype 的类型推导:decltype(auto),auto 表示类型需要推导,decltype 表示使用 decltype 规则进行推导,我们修改函数实现如下:

template<typename Container, typename Index>   // C++14; works, but still requires refinement
decltype(auto) authAndAccess(Container& c, Index i)
{authenticateUser();return c[i];
}

这里,authAndAccess 将真正返回 c[i] 的类型,也即 T&

decltype(auto) 不限于用在函数返回值,也可以用在变量申明,例如:

Widget w;
const Widget& cw = w;
auto myWidget1 = cw;    // auto type deduction: myWidget1's type is Widgetdecltype(auto) myWidget2 = cw;   // decltype type deduction: myWidget2's type is const Widget&

还有两点需要进一步讨论下,第一点是上面提到的 authAndAccess 还可以进一步优化的事情。让我们再看下 authAndAccess 的声明:

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

这里,Container 传参是一个 lvalue-reference-to-non-const,返回值的类型是可以被修改的,这也就意味着我们不能传右值 containers,右值不能绑定到左值引用(除非是 lvalue-references-to-const )。

一个比较好的解决方案是万能引用!如下:

template<typename Container, typename Index>
decltype(auto) authAndAccess(Container&& c, Index i);    // c is now a universal reference

在这个模板中,既可以传左值,也可以传右值。但是这里有一个不好地方上是:值传递会造成不必要的拷贝。

为了遵循 Item25 中的警告,我们需要修改下如下:

template<typename Container, typename Index>  // final C++14 version
decltype(auto)
authAndAccess(Container&& c, Index i)
{authenticateUser();return std::forward<Container>(c)[i];
}

以上需要 C++14 的编译器,C++11 版本的写法:

template<typename Container, typename Index>   // final C++11 version
auto authAndAccess(Container&& c, Index i)
-> decltype(std::forward<Container>(c)[i])
{authenticateUser();return std::forward<Container>(c)[i];
}

另一问题是开头说的少数场景下,decltype 可能返回不是你所预期的结果。对于一个左值表达式 expr,如果 expr 的类型是 Tdecltype(expr) 返回的是 T&

int x = 0;

decltype(x)int。但是对于 (x) 是一个左值表达式,则decltype((x)) 得到 int&

decltype(auto) f1()
{int x = 0;...return x;    // decltype(x) is int, so f1 returns int
}decltype(auto) f2()
{int x = 0;...return (x);    // decltype((x)) is int&, so f2 returns int&
}

f2f1 的返回值不同,f2 返回了一个局部变量的引用,这是相当危险的。

因此,在使用 decltype 的时候,建议使用 Item4 中的方法进行检查,确保得到你期望的类型推导。

最后,总结如下:

  • decltype几乎总是能够得出变量或者表达式的类型。
  • 对于类型为 T 的左值表达式,而不是名字,decltype 基本上总是输出 T&
  • C++14支持 delctype(auto),像是 auto,能够自动从初始化列表中推断出类型,但它使用的是decltype 的推断规则。

Item 3: Understand decltype相关推荐

  1. Item 1: Understand template type deduction

    Item 1: Understand template type deduction Case 1: ParamType is a Reference or Pointer, but not a Un ...

  2. Item 28: Understand reference collapsing.

    Item 28: Understand reference collapsing. 引用折叠规则 万能引用的实例化 std::forward 机制 生成 auto 变量 typedef 类型别名 de ...

  3. Item 2: Understand auto type deduction.

    Item 2: Understand auto type deduction 这次是针对 Effective Modern C++ Item2的学习笔记. 这次学习 auto 类型的自动推导,在 au ...

  4. Effective Modern C++ 第一章 C++11/14/17中的类型推断

    Chapter 1, Deducing Type Item 1: Template type deduction 一些基础知识: 关于左值和右值的一些解释:https://book.2cto.com/ ...

  5. VC GDI+: error C2660: 'new' : function does not take 3 parameters

    今天在用GDI+写程序时,有 bmp = new Bitmap(L"E:\\1.png"); 用VC6 SP6或VS2005编译错误为error C2660: 'new' : fu ...

  6. Effective Objective-C [下]

    http://esoftmobile.com/2013/08/17/effective-objective-c-2/ Chapter 6: Blocks and Grand Central Dispa ...

  7. 《Effective C#》Part I:第一部分总结

    第一部分是语言使用的基础,而这是使用语言的基本功,也是平常最不注意的,养成好的习惯,有利于后期水平提高. 这部分包括前面11个单元,分别如下: Item 1:   Always Use Propert ...

  8. 《Effective Modern C++》翻译--条款4:了解怎样查看推导出的类型

    条款4:了解怎样查看推导出的类型 那些想要了解编译器怎样推导出的类型的人通常分为两个阵营. 第一种阵营是实用主义者.他们的动力通常来自于编敲代码过程中(比如他们还在调试解决中),他们利用编译器进行寻找 ...

  9. 四种方式话Equal

    Item 9: Understand the Relationships Among ReferenceEquals(), static Equals(), instance Equals(), an ...

最新文章

  1. iOS 项目中用到的一些开源库和第三方组件
  2. C++设计模式-单例模式(双重锁定)
  3. popen() 函数 讲解
  4. linux yum 目录在哪,急问怎么知道yum从哪个地址下载的文件呢?
  5. token 微信access 过期_如何设计 QQ、微信等第三方账号登陆 ?以及设计数据库表!...
  6. 【Docker】常用命令简述十一
  7. jar包可以到maven下载
  8. 2016 网易校招内推C/C++第二场8.6
  9. Phoenix Tips (13) 统计收集
  10. Inception-ResNet-v1网络结构
  11. adb获取剪贴板内容_Android复制粘贴剪切板内容的一种方法
  12. BEGAN(Boundary Equilibrium GenerativeAdversarial Networks)-pyTorch实现
  13. 艾宾浩斯曲线真的管用吗?
  14. 安恒2018.10 level1思路讲解
  15. 压力变送器的特点及用途与维护
  16. Clickhouse备份恢复方式概览
  17. 01 二叉树的BFS(广度、层次或水平遍历实现)【Binary Tree 二叉树】
  18. 眼镜计算机检查,配眼镜时电脑验光到底准不准?
  19. 解决vmware 本地连接时出错:地址仍未与网络终结点关联
  20. Java学习路线:day11 客户信息管理软件

热门文章

  1. 透彻理解SLAM中的非线性最小二乘问题
  2. CSDN博客炫丽图标调整字体大小和颜色
  3. Linux下2D、3D的测试软件glxgears
  4. 善用EFI系统分区ESP
  5. 学shell和python哪个难_shell与python的优劣对比到底用哪个
  6. 如何以管理员身份运行命令提示符或终端窗口
  7. linux 蓝牙发送文件,如何在Ubuntu上使用蓝牙进行文件传输
  8. .NET MVC 后台接受base64的上传图片
  9. 第二周python牛客练习题
  10. 如何让暴风影音播放flv文件