我是靠谱客的博主 优美热狗,最近开发中收集的这篇文章主要介绍Item 24: Distinguish universal references from rvalue references.,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

Item 24: Distinguish universal references from rvalue references.

Effective Modern C++ Item 24 的学习和解读。

在 C++11 移动语义出现后,遇到 T&& ,你可能认为就是右值引用,其实不然,这可能是一个万能引用(universal reference),右值引用和万能引用只是形式上相似而已,二者实际上是两个概念。右值引用只能绑定到一个右值上;而万能引用既可以绑定到一个右值,也可以绑定到一个左值。另外,万能引用能绑定到 const 或非 const 对象,也能绑定到 volatile 或非 volatile 对象,甚至能绑定到 const 加 volatile 的对象。

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

万能引用主要有两种形式,一种是作为函数模版参数:

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

另一种是使用 auto 进行类型推导:

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

两种形式有一个共同点,那就是形式为 T&& 或者 auto&&,并且存在类型推导。像下面的代码就不是万能引用:

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

万能引用是一种引用,它必须被初始化。万能引用的初始化决定了它代表一个右值还是一个左值。如果初始化为一个右值,万能引用对应右值引用。如果初始化为一个左值,万能引用对应一个左值引用。

template<typename T>
void f(T&& param); // param is a universal reference
Widget 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&& 或者 auto&& (当然 T 只是符号,也可以是 type&& 等等),并且存在类型推导。

template<typename T>
void f(std::vector<T>&& param); // param is an rvalue reference
std::vector<int> v;
f(v); // error! can't bind lvalue to rvalue reference

这个例子中,如果调用点没有显示指明类型,也会发生类型推导。但它的形式不是 T&&,而是 std::vector&&,只是右值引用,若传递一个左值给 f ,将发生编译报错。

T&& 的形式是要求非常严格的,哪怕加一个 const 在前面,就会变成右值引用:

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

还需要注意,存在形式完全是 T&&,但因为不存在类型推导而不是万能引用的例子,std::vector 的 push_back 就是这样的情况:

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

push_back 的参数完全符合万能引用的形式,但是没有类型推导发生。因为 push_back 不能存在于vector 的特定实例之外,并且实例的类型就完全决定了 push_back 的声明类型。

std::vector<Widget> v;
// declare
// causes the std::vector template to be instantiated as follows:
class vector<Widget, allocator<Widget>> {
public:
void push_back(Widget&& x); // rvalue reference};

std::vector 中和 push_back 概念上相似的 emplace_back 用到了类型推导:

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

类型参数 Args (Args 其实是一个参数包,不是一个类型参数,可以把它视为一个类型参数。)独立于 vector 的类型参数 T,所以每次 emplace_back 被调用的时,Args 必须被推导,因而这里是万能引用。

auto&& 形式的变量也发生类型推导,也是万能引用。auto 形式的万能引用在 C++14 的 lambda 表达式中非常常见:

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

func 是一个万能引用,它能被绑定到任何调用的对象上,不管是左值还是右值。params 也是一个万能引用,它能被绑定到任何数量的任意类型的对象上去。

最后总结一下:

  • 如果一个函数模板参数有 T&& 格式,并且发生类型推导,或者一个对象使用 auto&& 来声明,那么参数或对象就是一个万能引用。
  • 如果类型推导的格式不是准确的 T&&(type&&),或者如果类型推导没有发生,T&&(type&&)就是一个右值引用。
  • 如果用右值来初始化,万能引用相当于右值引用。如果用左值来初始化,则相当于左值引用。

最后

以上就是优美热狗为你收集整理的Item 24: Distinguish universal references from rvalue references.的全部内容,希望文章能够帮你解决Item 24: Distinguish universal references from rvalue references.所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(64)

评论列表共有 0 条评论

立即
投稿
返回
顶部