万能引用

定义

万能引用(Universal Reference)是指在模板函数auto推导中,形如T&&的参数。当编译器通过类型推导确定T时,T&&可以绑定到左值或右值

1.函数模板:

1
2
3
template <typename T>
void func(T&& arg) {
}
  • 如果arg是左值,T会被推导为左值引用类型(如int&),因此T&&会折叠成int& &,简化为int&
  • 如果arg是右值,T会被推导为普通类型(如int),因此T&&就是int&&

2.auto:

1
auto&& x = some_value; // x 是万能引用
  • 如果some_value是左值,auto&&会推导为左值引用类型(如int&
  • 如果some_value是右值,auto&&会推导为右值引用类型(如int&&

在写for循环的时候,如果不确定输入的到底是左值还是右值,也可以用万能引用

  1. for(auto& user : users) - 引用左值:
    • 当你想要修改容器中的元素,并且users是一个左值时,使用引用引用(auto&)。
    • 这允许你在循环中直接修改元素。
  2. for(auto user : users) - 拷贝值:
    • 当你不需要修改容器中的元素,或者users是一个右值时,使用值语义(auto)。
    • 这会导致容器中的每个元素被拷贝到循环变量user中。
  3. for(auto&& user : users) - 引用右值(==万能引用==):
    • 这种形式通常用于通用编程,特别是当你不知道容器是左值还是右值时。
    • 如果users是左值,auto&&会被推导为auto&,即引用左值。
    • 如果users是右值,auto&&会被推导为auto,即值语义,但注意,这不会触发移动语义,因为在范围for循环中,元素不会被移动。

注意:万能引用只能用于模板函数或者auto,如果不是这种场合,则变成右值引用

1
2
3
void func(int&& arg) {
// arg 是右值引用,只能绑定到右值
}

完美转发

定义

完美转发用于将函数参数以最优的方式传递到另一个函数,同时保持参数的值类别(左值或右值)。它确保了传递的参数不会经历不必要的复制或者不正确的值类别转换。

完美转发只能用于模板函数,并结合万能引用和std::forward()

核心思想

当我们在某个函数中接受参数时,通常希望将这些参数转发给另一个函数,而不改变它们的值类别。如果传递的是左值,应该保持为左值;如果传递的是右值,应该保持为右值。

  • 完美转发利用了万能引用std::forward来实现这一点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <utility> // for std::forward

void process(int& x) {
std::cout << "Lvalue reference: " << x << std::endl;
}

void process(int&& x) {
std::cout << "Rvalue reference: " << x << std::endl;
}

template <typename T>
void forwarder(T&& arg) {
// 使用std::forward确保传递参数的原始左右值属性
process(std::forward<T>(arg));
}

int main() {
int a = 42;

forwarder(a); // arg是左值引用
forwarder(42); // arg是右值引用
forwarder(std::move(a)); // arg是右值引用
}

关键点

  1. 万能引用:T&& 在模板函数中可以绑定左值和右值,但只有通过 std::forward 才能实现完美转发
  2. std::forward<T>:这是一个标准库模板函数,用于在完美转发中保留参数的值类别。它根据传入的类型推导决定是转发为左值还是右值
    • 如果传递给std::forward<T>(arg)的是左值,std::forward<T>(arg)会转发为左值
    • 如果传递的是右值,std::forward<T>(arg)会转发为右值

使用场景

1.实现转发函数: 假设我们写了一个模板函数,这个函数的作用是转发其参数到另一个函数,并保持参数的值类别(左值或右值)

1
2
3
4
template <typename T>
void forward_to_another_func(T&& arg) {
another_func(std::forward<T>(arg));
}

2.转发构造函数参数: 在类模板中,常常需要将构造函数的参数转发给其他成员函数或构造函数。为了避免不必要的复制或类型转换,通常会使用完美转发。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class MyClass {
public:
template <typename T>
MyClass(T&& arg) {
// 使用完美转发将构造函数参数转发给成员函数
m_member_func(std::forward<T>(arg));
}

private:
void m_member_func(int& x) {
std::cout << "Lvalue: " << x << std::endl;
}

void m_member_func(int&& x) {
std::cout << "Rvalue: " << x << std::endl;
}
};