为了声明指向某类型T的右值引用,你会写T&&。这会使我们在看到源码种出现“T&&”时,就认为是一个右值引用。但,没那么简单:

void f(Widget&& param);             // rvalue referenceWidget&& var1 = Widget();           // rvalue referenceauto&& var2 = var1;                 // not rvalue referencetemplate<typename T>
void f(std::vector<T>&& param);     // rvalue referencetemplate<typename T>
void f(T&& param);                  // not rvalue reference

事实上,“T&&” 有两种不同的涵义。当然,其中一个是右值引用的意思。这种引用行为就是你所期望的:它们只绑定到右值上去,并且它们的主要职责就是去明确一个对象是可以被move的。
“T&&” 的另一种含义是,则表示其既可以是右值引用,也可以是左值引用。带有这种含义的引用在代码中形如右值引用(即 “T&&”),但它们能表现的像左值引用一样(即 “T&”)。这种双重特性使之既可以绑定到右值(如右值引用),也可以绑定到左值(左值引用)。此外,它们也可以绑定到const对象或非const对象,以及volatile对象或非volatile对象,甚至绑定到既带有const又带有volatile属性的对象。它们几乎可以绑定到任何东西。我们称这种史无前例的灵活性的引用为万能引用【Item 25将会解释万能应用几乎总是需要应用std::forward。目前C++ 委员会的一些成员开始称其为转发引用(forwarding references)】。

万能引用出现在两种场景下。最常用的场景是函数模板参数,比如上面的代码:

template<typename T>
void f(T&& param);              // param is a universal reference

另外一种场景是auto声明,比如上面代码中的:

auto&& var2 = var1;             // var2 is a universal reference

这两种场景的共同之处,在于它们都涉及到类型推导。在模板f中,param的类型是推导得到的,而在var2的声明语句中,var2的类型也是推导得到的。相比之下,下面的例子就不涉及到类型推导。如果你看到没有类型推导的 “T&&” 时,那么就是右值引用:

void f(Widget&& param);         // no type deduction;// param is an rvalue referenceWidget&& var1 = Widget();       // no type deduction;// var1 is an rvalue reference

因为万能引用也是引用,所以它必须被初始化。万能应用的初始化物(initializer)决定了它代表的是左值引用还是右值引用。如果initializer是右值,万能引用相当于右值引用,如果initializer是左值,则万能应用相当于左值引用。对于作为函数形参的万能引用而言,初始化物(initializer)在调用出提供:

template<typename T>
void f(T&& param);            // param is a universal referenceWidget w;
f(w);                         // lvalue passed to f; param's type is// Widget& (i.e., an lvalue reference)f(std::move(w));              // rvalue passed to f; param's type is// Widget&& (i.e., an rvalue reference)

要使一个引用成为万能引用,类型推导是必要非充分条件。引用声明的形式也必须正确无误,且该形式被限定的很死:必须形如 “T&&” 才行【换一种好理解的表述:如果函数模板形参具备T&&格式,且T类型是推导而来,或者对象使用auto&&声明其类型,则该形参或对象就是个万能引用】。再看一次这个我们之前在示例代码中看过的例子:

template<typename T>
void f(std::vector<T>&& param);  // param is an rvalue reference

当f被调用时,类型T将被推导(除非调用者显示指明了类型,这是一种我们不必关心的边界情况),但paramr的类型声明形式不是 “T&&”,而是 “std::vector&&”。这就排除了param是万能引用的可能性。因此,param是一个右值引用,如果你尝试向f传递左值,编译器会很乐意为你确认出来:

std::vector<int> v;
f(v);                       // error! can't bind lvalue to// rvalue reference

即使是一个const修饰的存在,也足以褫夺一个引用成为万能引用的资格:

template<typename T>
void f(const T&& param);    // param is an rvalue reference

如果在模板内看到一个函数的形参类型写作 “T&&”,你可能想当然的认为它肯定是一个哇能引用。其实不然。因为位于模板内并不保证一定涉及类型推导,考虑如下代码:

template<class T, class Allocator = allocator<T>>   // from C++
class vector {                                      // Standards
public:void push_back(T&& x);…
};

push_back的形参格式虽然符合万能引用的格式,但是并不涉及类型推导。因为push_back不能存在于vector的特定实例之外,并且实例的类型就完全能决定push_back的声明类型了。也就是说:

std::vector<Widget> v;

使得std::vector模板被实例化为下面这样:

class vector<Widget, allocator<Widget>> {public:void push_back(Widget&& x);             // rvalue reference…
};

现在你能清楚地发现push_back没有用到类型推导。所以这个push_back是指向T的右值引用。

std::vector中还有一个与push_back概念类似的emplace_back就涉及到了类型推导:

template<class T, class Allocator = allocator<T>>   // still from
class vector {                                      // C++
public:                                             // Standardstemplate <class... Args>void emplace_back(Args&&... args);…
};

在这里,类型参数Args独立于vector的类型参数T,所以每次emplace_back被调用的时候,Args必须被推导。(好吧,Args事实上是一个参数包,不是一个类型参数,但是为了讨论的目的,我们能把它视为一个类型参数。)

前面提到过aoto变量也可以作为万能引用。准确地说,声明为auto&&类型的变量都是万能引用,因为它们既涉及到类型推导,也有正确的格式(“T&&”)。 auto万能引用在C++ 11中没有像函数模板形参的万能引用那么常见,但是在C++ 14中却经常出现,尤其是写一个lambda表达式:

auto timeFuncInvocation =[](auto&& func, auto&&... params)                       // C++14{// start timer;std::forward<decltype(func)>(func)(                 // invoke funcstd::forward<decltype(params)>(params)...       // on params);// stop timer and record elapsed time;};

也许你会对“std::forward<decltype(blah blah blah)>”这种形式的代码迷惑,不过没关系,Item 33中会有详细展示。

其实呢,本条款所说的万能引用是一个抽象的说法,它底层的真相被称为“引用折叠”。Item 28会专门讲这个问题。之所以本条款讨论了万能引用和右值引用的区别,目的是为了我们能更精准的阅读代码(“我看到的T&&只能绑定到右值上,还是能绑定到所有东西上呢?”),并且在你和同事讨论的时候,它能让你避免歧义。(“我在这里使用一个universal引用,不是一个右值引用…”)。它也能让你搞懂Item 25和Item 26的意思,这两个Item都依赖于这两个引用的区别。另外,掌握万能引用的概念会比了解引用折叠的技术细节是个更好的选择。

Things to Remember

  • 如果函数模板形参具备T&&格式,并且T类型是推导而来,或者对象使用auto&&声明其类型,则该形参或对象就是个万能引用;
  • 如果类型声明并不精确的匹配type&&格式,类型推导没有发生,type&&就代表右值引用;
  • 如果采用右值初始化万能引用,就会得到一个右值引用;如果用左值初始化万能引用,就会得到一个左值引用;

Item 24: Distinguish universal references from rvalue references相关推荐

  1. C++11中rvalue references的使用

    Rvalue references are a feature of C++ that was added with the C++11 standard. The syntax of an rval ...

  2. Rvalue References

    Rvalue References

  3. item 24: 区分右值引用和universal引用

    本文翻译自<effective modern C++>,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 博客已经迁移到这里啦 古人曾说事情的真相会让你觉得很自在,但是在适当的情 ...

  4. Service References和Web References的引用

    引用WebService方式有两种 1.Service References 添加服务引用 -------->输入webservice地址点击转到会出现命名空间对应的服务(可以进行修改命名空间) ...

  5. 《Effective Morden C++》Item 7: Distinguish between () and {} when creating objects

    引子 从本Item开始,我们就进入了第二个章节,本章节,S.M.通过对比C++的新老feature来鼓励大家使用new features. 本Item先从对象初始化开始. 正文 1.基础 C++11中 ...

  6. 深入理解java中的Soft references amp;amp; Weak references amp;amp; Phantom reference

    引言 Ethan Nicholas 在他的一篇文章中说:他面试了20多个Java高级工程师,他们每个人都至少有5年的Java从业经验,当他问这些工程师对于Weak References 的理解时,只有 ...

  7. JVM 内存分析神器 MAT: Incoming Vs Outgoing References 你真的了解吗?

    点击上方蓝色字体,选择"设为星标" 优质文章,及时送达 了解 Eclipse MAT 中 incoming and outgoing 引用之间的区别. Eclipse MAT(内存 ...

  8. 使用 .NET 平台,如何玩转 Universal Windows 应用?

    2015年7月30日 本文作者是 Managed Languages 团队项目经理 Lucian Wischik. 不久前,Visual Studio 2015上新增 Windows 10 应用的开发 ...

  9. 使用 .NET 平台,如何玩转 Universal Windows 应用? 1

    2015年7月30日 本文作者是 Managed Languages 团队项目经理 Lucian Wischik. 不久前,Visual Studio 2015上新增 Windows 10 应用的开发 ...

最新文章

  1. rinetd 做端口转发
  2. python if调用函数,Python根据字符串调用函数过程解析
  3. CSDN:解决粉丝网友集中问题留言处,把你所有的问题在留言处留言,我会一一回答
  4. flashfxp连mysql_FlashFXP 命令行参数
  5. 谭浩强课后题(数组篇)
  6. ubuntu、fedora系统的启动|关闭管理器
  7. java线程6种状态转换,Java线程的生命周期和各种状态转换详解
  8. 3.3.4.5. 日期计算
  9. 阿里java工具包_阿里开源的Java诊断工具Arthas(阿尔萨斯)
  10. Proxy server got bad address from remote server
  11. [jQuery案例练习]——锅打灰太狼
  12. 数据的力量 |《2021—2022中国大数据行业发展报告》发布
  13. Excel怎么批量设置图片大小
  14. ubuntu命令行模式与图形桌面切换方法
  15. CSS 基础教程:CSS 教程:什么是 CSS?
  16. 用Hive、Impala查询Hbase数据
  17. 字符串操作函数strstr
  18. 短信验证码内容组成及设计注意事项
  19. 多态 在游戏程序实例
  20. 如何利用Win7Aero特效来美化你的程序窗口

热门文章

  1. Linux 管道文件
  2. 2012服务器IP地址怎么修改,2012服务器IP地址怎么修改
  3. 使用VIM搜索多个文件
  4. 《魔比斯环》是国产的,惊喜
  5. 蝉花与冬虫夏草的比较
  6. Round3:我的黑白框雷霆战机进阶2
  7. 计算机如何存储中文字符
  8. Google面经,已拿到offer哦!
  9. R-因子分析(主成分提取法)
  10. 部署k8s ssl集群实践12:work节点配置docker