元芳你怎么看

本网站主要用于记录我个人学习的内容,希望对你有所帮助

0%

条款24:区分万能引用和右值引用

前言

不是所有的&&都是右值引用!

1
2
3
4
5
6
7
8
void f (Widget&& param);    // 右值引用
Widget&& varl = Widget(); // 右值引用
auto&& var2 = van; // 不是右值引用
template<typename T>
void f(std: :vector<T>&& param); // 右值引用
template<typename T>
void f(T&& param); // 不是右值引用

好了,看完CPU烧了,到底是不是右值引用呀,我也不知道呀。
实际上, “ T&& ”有两种不同的含义。其中一种含义,理所当然,是右值引用。正如期望,它们仅仅会绑定到右值,而其主要的存在理由,在于识别出可移对象。“ T&& “的另一种含义,则表示其既可以是右值引用,亦可以是左值引用。它可以绑定到右值,也可以像左值引用一样,也可以绑定在const对象或者非const对象,也可以绑定在volatile对象或者非volatile对象,它灵活度高到被成为万能引用

万能引用的场景

  1. 函数模板的推导
  2. auto声明
1
2
3
4
5
auto&& var2 = van;          // 不是右值引用

template<typename T>
void f(T&& param); // 不是右值引用
//在这种情况下,类型 T 是需要从参数中推导的,因为 param 的类型是未知的,这时编译器将根据参数类型来推导 T 的类型。

这两个场景的共同之处:都涉及型别推导。如果看到了T&&但是没有型别推导,那么就可以确定是右值引用。

万能引用的条件

万能引用的特点是它们具有两个条件:

  1. 必须在模板内进行型别推导:这意味着类型 T 必须在模板函数或模板类内部通过类型推导而不是显式指定。这是因为模板会根据实际传递给函数或类的参数类型来推导 T 的类型。

  2. 型别声明的形式必须T&&:这表示右值引用的声明必须以 T&& 的形式出现,以指示它可以成为万能引用。

如果这两个条件同时满足,那么右值引用就被认为是万能引用。

这也就是为什么上面提到的void f(std: :vector<T>&& param);是右值引用而不是万能引用。它虽然涉及型别推导,但它的型别声明形式不是 T&&,而是 std::vector<T>&&。因此,这只是一个右值引用,而不是万能引用。

即使只是一个const的存在,也不满足条件2,就成为了右值引用。

1
2
template<typename T>
void f(const T&& param); //右值引用

如果在一个模板内看到一个函数形参为T&&也不能想当然的认为它是万能引用。看下面的例子

1
2
3
4
5
6
using namespace std;
template<class T , class Allocator = allocator<T>>
class vector{
public:
void push_back(T&& x);
};

push_back的形参满足条件,但是条件一:型别推导,它并没有涉及。因为该函数作为vector类的一部分,如果没有特定vector类实例,就没有该函数(C++类的基础)。如果给定一个vector实例,那么push_back的形参就会被具体化。比如给定一个实例:

1
std::vector<Entity> entity;

vector模板就会被具化为:

1
2
3
4
class vector<Entity, allocator<Entity>>{
public:
void push_back(Entity&& x);
};

可以看到,push_back函数并没有涉及类型推导。因此其参数不是万能引用。

1
2
3
4
5
6
template<class T , class Allocator = allocator<T>>
class vector{
public:
template<class... Args>
void emplace_back(Args&&... args); // 万能引用
};

这里的args涉及到了类型推导,因此是万能引用。

多说一点

有些人不清楚template<class... Args>中的...是什么,其实很简单,”…” 是用来表示可变参数模板的语法。它用于表示函数或类模板可以接受可变数量的参数。具体来说,Args 是一个参数包,它可以包含零个或多个模板参数。Args&&... args 表示您可以将任意数量的参数传递给 emplace_back 函数,每个参数都会被表示为 Args 类型的右值引用。这种语法通常用于实现”完美转发”。(后面会提到)

而前面提到的另一种场景,auto声明,在C++11中不怎么普遍。而在C++14中,它现身的机会就更多了,因为C++14中的lambda表达式中可以声明auto&& 的形参。

万能引用的作用

这种引用在模板编程中非常有用,因为它们能够保持对传递给它们的参数的左值或右值性质,并且可以实现完美转发,使它们适用于各种不同类型的参数。