什么是万能引用
“万能引用”是 Scott Meyers 在他的书《Effective Modern C++》中引入的术语,用于描述某些模板参数的行为,这种模板参数可以绑定到几乎任何类型的参数,无论是 lvalue 还是 rvalue。正式的名称是"转发引用"(forwarding reference)。
一个万能引用的定义形式如下:
1template<typename T>
2void function(T&& arg);
这里,T&& 不仅仅是一个普通的右值引用。根据传递给函数的参数类型,它可能会表示一个 lvalue 引用或一个 rvalue 引用。这是通过模板参数推导和引用折叠规则实现的。
考虑以下情况:
- 传递 lvalue:
1int a = 42;
2function(a); // 'a' is an lvalue
在这种情况下,T 被推导为 int&,所以 T&& 实际上变为 int& &&。根据引用折叠规则,它折叠为 int&。因此,arg 是一个指向 a 的 lvalue 引用。
- 传递 rvalue:
1function(42); // '42' is an rvalue
在这种情况下,T 被推导为 int,所以 T&& 保持为 int&&。因此,arg 是一个 rvalue 引用。
万能引用的一个主要用途是实现“完美转发”,即在模板中将参数转发到其他函数,同时保持其原始值类别(lvalue 或 rvalue)。这通常与 std::forward 函数模板结合使用,如:
1template<typename T>
2void forwarder(T&& arg) {
3 anotherFunction(std::forward<T>(arg));
4}
这样,无论传递给 forwarder 的是 lvalue 还是 rvalue,都会以相同的方式转发给 anotherFunction。
万能引用和右值引用的区别是什么
万能引用(也称为转发引用)和纯右值引用在语法上看起来很像,都使用 &&,但它们在语境和行为上有显著的不同。以下是它们之间的主要区别:
-
定义上的区别:
- 万能引用:只能作为函数模板参数,并且当其类型是以模板参数形式推导出来的时才被认为是万能引用。例如,
template<typename T> void func(T&& arg);这里的T&&是一个万能引用。 - 右值引用:在非模板代码或模板代码中,如果引用的类型不是由推导确定的,那么它是一个纯右值引用。例如,
int&& r = 5;这里的int&&是一个纯右值引用。
- 万能引用:只能作为函数模板参数,并且当其类型是以模板参数形式推导出来的时才被认为是万能引用。例如,
-
行为上的区别:
- 万能引用:可以绑定到 lvalue 和 rvalue。模板类型推导和引用折叠规则决定了它最终的类型。如果传入的是 lvalue,
T会被推导为 lvalue 引用,使得T&&成为 lvalue 引用;如果传入的是 rvalue,T被推导为非引用类型,使得T&&保持为 rvalue 引用。 - 右值引用:只能绑定到临时对象或被转换为 rvalue 的对象。它的主要目的是支持移动语义和临时对象的生命周期延长。
- 万能引用:可以绑定到 lvalue 和 rvalue。模板类型推导和引用折叠规则决定了它最终的类型。如果传入的是 lvalue,
-
用途上的区别:
- 万能引用:主要用于"完美转发",即无论传入的是 lvalue 还是 rvalue,都可以将其转发给另一个函数,同时保持原始的值类别。为此,通常与
std::forward一起使用。 - 右值引用:主要用于实现移动语义,即从一个对象“移动”其资源而不是复制。这在大对象或资源有限的场合(如动态分配的内存)中非常有用,因为移动通常比复制更有效率。
- 万能引用:主要用于"完美转发",即无论传入的是 lvalue 还是 rvalue,都可以将其转发给另一个函数,同时保持原始的值类别。为此,通常与
总的来说,万能引用是模板编程中的一个技巧,允许函数模板根据传入的参数类型动态地选择引用类型(lvalue 引用或 rvalue 引用),而右值引用是 C++11 引入的新型别,用于支持移动语义和对临时对象的高效操作。