IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 《Effective Modern C++》学习笔记 - Item 3: 理解decltype -> 正文阅读

[C++知识库]《Effective Modern C++》学习笔记 - Item 3: 理解decltype

  • 作用:给出一个变量名或表达式,decltype 返回变量或表达式的类型。偶尔,decltype返回的结果会让你非常迷惑。
  • 先从正常的例子看起。不像模板和 auto 的类型推导,decltype 一般返回的就是准确的类型:
const int i = 0; 			// decltype(i) is const int
bool 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 int

Widget w; 					// decltype(w) is Widget
if (f(w)) 	 				// decltype(f(w)) is bool

vector<int> v; 				// decltype(v) is vector<int>
if (v[0] == 0) 				// decltype(v[0]) is int&
  • 在C++11中,decltype 的最初用途也许是声明返回值依赖于参数类型的函数模板。例如我们要写一个函数,该函数接受一个容器变量c和一个索引i,函数中先调用某个其它函数,然后返回c[i]。此时函数返回值类型取决于容器元素的类型,使用 decltype 可以轻松地表示出该语义:
// C++11版本,仍需改进
template<typename Container, typename Index>
auto authAndAccess(Container& c, Index i) -> decltype(c[i])
{
	authenticateUser();
	return c[i];
}
  • 这里使用的 auto 与类型推导无关,而是C++11的一种语法:返回值类型后置(trailing return type),即声明把函数返回值类型放到参数列表后面,这使得我们可以使用参数列表中的符号声明返回类型。本例中,我们结合该语法和 decltype 实现了把返回值类型定义为参数相关的类型。
  • 在C++14中,我们也可以去掉后置的返回类型,就让编译器去利用 auto 特性推断返回值类型:
// C++14版本,有问题
template<typename Container, typename Index>
auto authAndAccess(Container& c, Index i)
{
	authenticateUser();
	return c[i];
}
  • 以上程序问题在于:元素类型为 T 的容器的 operator [] 返回的类型一般是 T&,然而根据 Item 1,auto(实际上是按模板类型推导规则)推断出的返回类型会抹除引用性,导致返回值无法作为左值使用:
    在这里插入图片描述

authAndAccess的返回值类型是int,是一个右值

  • 为此,C++14提出了 decltype(auto) 标识符。乍一看可能感觉这两者互相矛盾,但实际上它的语义是符合逻辑的:auto 指明类型需要被推导,decltype 表示推导过程应使用 decltype(而不是模板推导)的规则。 改写如下:
// C++14版本,仍需改进
template<typename Container, typename Index>
decltype(auto) authAndAccess(Container& c, Index i)
{
	authenticateUser();
	return c[i];
}
  • decltype(auto) 的语法也适用于声明变量:
Widget w;
const Widget& cw = w;

auto myWidget1 = cw;			// myWidget1的类型是Widget
								// 因为没有修饰符的auto等价于模板推导的Case 3按值传递
								// 引用性和const被抹除
								
decltype(auto) myWidget2 = cw;	// myWidget2的类型是const Widget&, 正确
auto& myWidget3 = cw;			// 笔者注: auto&的写法也是ok的
								// 此时相当于模板推导的Case 1, const性会被保留到auto中

VS的贴心提示:
在这里插入图片描述

  • 至此,还可能让你有点心神不宁的就是那个“仍需改进”,现在就让我们来处理它。
  • 以上函数对容器的传参方式是左值的非常引用,因为我们允许用户对函数的返回值(也就是容器中的元素)进行修改。这带来一个问题:无法将右值作为参数传入。
  • 诚然,这是一种edge case,因为传入的右值容器对象在当前调用函数语句结束时就被销毁,而维持的对容器元素的引用就会“悬空”(dangle)。
  • 但还是有可能我们会想向函数中传入一个临时对象。例如,希望从临时容器中获得一个对象的复制:
std::deque<std::string> makeStringDeque(); // 工厂函数,生产deque对象
// 希望获得makeStringDeque创建的deque中第5个元素的一个复制
auto s = authAndAccess(makeStringDeque(), 5);
  • 为了支持这种功能,我们需要使函数同时能接受左值和右值参数。函数重载(Overloading)可行,但意味着我们要维护多一个函数。解决方法是使用同时可以绑定到左值和右值的万能引用来声明函数。
  • 在函数定义中,还需对万能引用应用Item 25所述的 std::forward
// C++14最终版本
template<typename Container, typename Index>
decltype(auto) authAndAccess(Container&& c, Index i)
{
	authenticateUser();
	return std::forward<Container>(c)[i];
}
// C++11最终版本
template<typename Container, typename Index>
auto authAndAccess(Container&& c, Index i) -> decltype(std::forward<Container>(c)[i])
{
 	authenticateUser();
 	return std::forward<Container>(c)[i];
}
  • decltype 的行为“几乎”总和你的预期一致,但还是有少数例外。这里举一个例子。
  • decltype 对于左值表达式(非单一变量名),其返回值总是左值引用。这基本没有问题,因为大多数左值表达式本身都隐含了引用标识符。然而考虑以下情况:
    在这里插入图片描述
    x 改为 (x) 时,后者被认为是一个表达式,因此使用 decltype 推断出 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不同,它返回了一个指向函数局部变量的引用!undefined behavior在向你招手…
  • 因此,使用 decltype(auto) 时要格外小心,因为一些看起来微不足道的改变就会带来完全错误的后果。

总结

  1. decltype 几乎总会返回一个变量或表达式的未经修改的类别。
  2. 对于非变量名的左值表达式,decltype 总是返回引用。
  3. C++14支持 decltype(auto),它使用 decltype 的规则进行类型推导。

笔者注:开始感受到C++的力量了吗(笑)

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-12-16 17:29:31  更:2021-12-16 17:30:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 12:37:34-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码