完美转发使用
完美转发的几个例子
例子 1:普通的完美转发
首先,我们先来一个简单的完美转发的例子,展示如何使用 std::forward 来保持传入参数的类型。
#include <iostream>
#include <utility>  // std::forward
void func(int& x) {
    std::cout << "Lvalue reference: " << x << std::endl;
}
void func(int&& x) {
    std::cout << "Rvalue reference: " << x << std::endl;
}
template <typename T>
void wrapper(T&& arg) {
    // 使用完美转发
    func(std::forward<T>(arg));  // 根据传入的类型精确地调用对应的函数
}
int main() {
    int x = 10;
    wrapper(x);   // 左值传递
    wrapper(20);  // 右值传递
}
 
输出:
Lvalue reference: 10
Rvalue reference: 20
 
在这个例子中:
wrapper(x)传入了左值,std::forward<T>(arg)将arg保持为左值,并转发到func(int& x)。wrapper(20)传入了右值,std::forward<T>(arg)将arg保持为右值,并转发到func(int&& x)。
关键点:
std::forward<T>(arg)确保了arg的类型(左值或右值)在转发时不丢失。
例子 2:完美转发与引用的结合
有时你可能希望通过完美转发将引用类型的参数转发给另一个函数。我们通过以下例子来演示这一点:
#include <iostream>
#include <utility>
void process(int& x) {
    std::cout << "Processing left value: " << x << std::endl;
}
void process(int&& x) {
    std::cout << "Processing right value: " << x << std::endl;
}
template <typename T>
void handler(T&& arg) {
    process(std::forward<T>(arg));  // 完美转发
}
int main() {
    int a = 10;
    handler(a);        // 左值
    handler(20);       // 右值
}
 
输出:
Processing left value: 10
Processing right value: 20
 
handler(a)将左值a转发给process(int& x)。handler(20)将右值20转发给process(int&& x)。
例子 3:多个参数的完美转发
完美转发不仅适用于一个参数,还可以应用于多个参数。这是通过递归和 std::forward 的组合来实现的。
#include <iostream>
#include <utility>
void process(int& a, double& b) {
    std::cout << "Processing left values: " << a << ", " << b << std::endl;
}
void process(int&& a, double&& b) {
    std::cout << "Processing right values: " << a << ", " << b << std::endl;
}
template <typename T, typename U>
void wrapper(T&& arg1, U&& arg2) {
    process(std::forward<T>(arg1), std::forward<U>(arg2));  // 完美转发
}
int main() {
    int x = 5;
    double y = 3.14;
    wrapper(x, y);      // 左值
    wrapper(10, 2.718);  // 右值
}
 
输出:
Processing left values: 5, 3.14
Processing right values: 10, 2.718
 
wrapper(x, y)转发左值。wrapper(10, 2.718)转发右值。
例子 4:通过完美转发转发容器
在处理容器时,完美转发也很有用。以下是一个将容器对象通过完美转发传递给函数的例子:
#include <iostream>
#include <vector>
#include <utility>
void process(std::vector<int>& vec) {
    std::cout << "Lvalue reference to vector: ";
    for (auto v : vec) std::cout << v << " ";
    std::cout << std::endl;
}
void process(std::vector<int>&& vec) {
    std::cout << "Rvalue reference to vector: ";
    for (auto v : vec) std::cout << v << " ";
    std::cout << std::endl;
}
template <typename T>
void wrapper(T&& arg) {
    process(std::forward<T>(arg));  // 完美转发
}
int main() {
    std::vector<int> vec = {1, 2, 3};
    wrapper(vec);         // 左值
    wrapper(std::vector<int>{4, 5, 6});  // 右值
}
 
输出:
Lvalue reference to vector: 1 2 3 
Rvalue reference to vector: 4 5 6
 
wrapper(vec)转发左值vec。wrapper(std::vector<int>{4, 5, 6})转发右值。
为什么命名为 std::forward?
 
std::forward 的命名源于它的用途——“转发”一个参数。这个名称可以追溯到它的功能和它的语义:
-  
“Forward” 表示转发:
std::forward的主要目的是精确转发一个参数,保持参数原本的值类别(左值或右值)。它“向前”转发参数,就像把参数从一个函数“传递”到另一个函数。 -  
与
std::move区别:std::move让对象变成右值,而std::forward保证保持参数的原始类型(左值或右值)。std::move的命名非常直观,因为它的作用是“移动”资源。而std::forward的命名则代表“保持原样,准确转发”。 -  
保证类型属性不变:
std::forward使用类型推导机制(模板参数T)来决定传递给目标函数的参数是左值还是右值。这使得它能“转发”参数并保持原始类型的属性,不会做多余的修改。 
总结
- 完美转发使用 
std::forward来确保参数传递时类型(左值或右值)保持不变。 std::forward通过模板参数类型T,结合条件判断(左值或右值),确保正确地转发参数。- “Forward” 这个命名意味着它是一个精确的“转发”工具,它转发的是一个函数的参数,并且保留了参数的原始类型属性。
 
