Item 3: Understand decltype
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 的类型是 T,decltype(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&
}
f2 和 f1 的返回值不同,f2 返回了一个局部变量的引用,这是相当危险的。
因此,在使用 decltype 的时候,建议使用 Item4 中的方法进行检查,确保得到你期望的类型推导。
最后,总结如下:
- decltype几乎总是能够得出变量或者表达式的类型。
- 对于类型为 T 的左值表达式,而不是名字,decltype 基本上总是输出 T&。
- C++14支持 delctype(auto),像是 auto,能够自动从初始化列表中推断出类型,但它使用的是decltype 的推断规则。
Item 3: Understand decltype相关推荐
- Item 1: Understand template type deduction
Item 1: Understand template type deduction Case 1: ParamType is a Reference or Pointer, but not a Un ...
- Item 28: Understand reference collapsing.
Item 28: Understand reference collapsing. 引用折叠规则 万能引用的实例化 std::forward 机制 生成 auto 变量 typedef 类型别名 de ...
- Item 2: Understand auto type deduction.
Item 2: Understand auto type deduction 这次是针对 Effective Modern C++ Item2的学习笔记. 这次学习 auto 类型的自动推导,在 au ...
- Effective Modern C++ 第一章 C++11/14/17中的类型推断
Chapter 1, Deducing Type Item 1: Template type deduction 一些基础知识: 关于左值和右值的一些解释:https://book.2cto.com/ ...
- 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 ...
- Effective Objective-C [下]
http://esoftmobile.com/2013/08/17/effective-objective-c-2/ Chapter 6: Blocks and Grand Central Dispa ...
- 《Effective C#》Part I:第一部分总结
第一部分是语言使用的基础,而这是使用语言的基本功,也是平常最不注意的,养成好的习惯,有利于后期水平提高. 这部分包括前面11个单元,分别如下: Item 1: Always Use Propert ...
- 《Effective Modern C++》翻译--条款4:了解怎样查看推导出的类型
条款4:了解怎样查看推导出的类型 那些想要了解编译器怎样推导出的类型的人通常分为两个阵营. 第一种阵营是实用主义者.他们的动力通常来自于编敲代码过程中(比如他们还在调试解决中),他们利用编译器进行寻找 ...
- 四种方式话Equal
Item 9: Understand the Relationships Among ReferenceEquals(), static Equals(), instance Equals(), an ...
最新文章
- iOS 项目中用到的一些开源库和第三方组件
- C++设计模式-单例模式(双重锁定)
- popen() 函数 讲解
- linux yum 目录在哪,急问怎么知道yum从哪个地址下载的文件呢?
- token 微信access 过期_如何设计 QQ、微信等第三方账号登陆 ?以及设计数据库表!...
- 【Docker】常用命令简述十一
- jar包可以到maven下载
- 2016 网易校招内推C/C++第二场8.6
- Phoenix Tips (13) 统计收集
- Inception-ResNet-v1网络结构
- adb获取剪贴板内容_Android复制粘贴剪切板内容的一种方法
- BEGAN(Boundary Equilibrium GenerativeAdversarial Networks)-pyTorch实现
- 艾宾浩斯曲线真的管用吗?
- 安恒2018.10 level1思路讲解
- 压力变送器的特点及用途与维护
- Clickhouse备份恢复方式概览
- 01 二叉树的BFS(广度、层次或水平遍历实现)【Binary Tree 二叉树】
- 眼镜计算机检查,配眼镜时电脑验光到底准不准?
- 解决vmware 本地连接时出错:地址仍未与网络终结点关联
- Java学习路线:day11 客户信息管理软件