C++11 完美转发

本文介绍了C++11中的右值引用、万能引用(模板参数)以及引用折叠和完美转发的概念,这些特性增强了对对象移动的支持,提高了程序效率。右值引用引入了移动语义,万能引用通过模板实现同时处理左值和右值,引用折叠则规定了多层引用折叠后的类型,而完美转发确保了参数原样传递给其他函数,保持其左值或右值性质。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C++技能点之右值引用、万能引用、引用折叠、完美转发


本篇博客为转载内容 原文链接,这里是做一个备份,请大家移步 原文进行阅读

1. 概述

在这里插入图片描述

2. 右值引用

  • 要了解什么是右值引用,首先要了解什么是右值。右值其实是一个相对的概念,与其相对的就是左值。左值与右值最大的区别就是左值是可以被取地址的,右值是不能被取地址的。举例来说:int a = 1;中,变量a就是左值,在内存中是有地址的,而1就是右值,是不能被取址的。
  • 对于引用类型来说,左值引用(&)和右值引用(&&)的表达方式不同。那么为什么C++11要强化右值的概念,引入右值引用呢?因为右值引用支持了移动语义,即可以不通过拷贝的方式,将值移动到新对象中。举例来说:来自 C++ Rvalue References Explained
    // 左值:
    //
    int i = 42;
    i = 43; // i 是左值
    int* p = &i; // i 是左值,因为可以被取址
    int& foo();
    foo() = 42; // foo() 是左值
    int* p1 = &foo(); // foo() 是左值
    string &s = "abc"; // error,右值不可以赋给左值
    
    // 右值:
    //
    int foobar();
    int j = 0;
    j = foobar(); //  foobar() 是右值
    int* p2 = &foobar(); // error, 右值不能被取址
    j = 42; // 42 是右值
    const string &s = "abc"; // 正确,因为常左值也可赋右值
    

3. 万能引用

先来看这样一段代码,fun函数同时支持了参数为左值和右值的情况

#include <iostream>
using namespace std;

// 接收左值
void fun(int& lvalue)
{
    std::cout << "lvalue = " << lvalue << std::endl; 
}
// 接收右值
void fun(int&& rvalue)
{
    std::cout << "rvalue = " << rvalue << std::endl; 
}

int main()
{
    int x = 10;
    fun(x);     // 左值
    fun(10);    // 右值
    fun(std::move(x));  // std::move(x) == static_cast<int&&>(x) 将左值转发为右值
}

这样每次需要重载两个函数实在是麻烦,有没有办法将两个函数写在一个里面呢?万能引用就是实现了功能,所谓万能引用就是同时支持左值参数和右值参数,而且万能引用并不是C++的一个新特性,而是我们自己所实现的功能。万能引用常见形式如下:

template<typename T>
void fun(T&& param)
{
    // do something
}

有人可能会有疑问,这不是参数接收右值么?怎么能接受左值的?答案是由于模板函数的类型推导导致的。如果传左值,假设为int&,传入参数中的类型就为int& &&,之后经由引用折叠导致最终等价于int&

4. 折叠引用

firstsecondresult
&&左值
&&&左值
&&&左值
&&&&右值

可以看出引用折叠的规则即任意一个引用为左值引用时结果均为左值,只有在两个引用均为右值引用时才是右值引用。

5. 完美转发

有了引用折叠之后,我们将之前的代码重新改写成:

#include <iostream>
using namespace std;

// 接收左值
void fun(int& lvalue)
{
    std::cout << "lvalue = " << lvalue << std::endl; 
}
// 接收右值
void fun(int&& rvalue)
{
    std::cout << "rvalue = " << rvalue << std::endl; 
}
// 万能引用
template<typename T>
void function(T&& param)
{
    fun(param);
}

int main()
{
    int x = 10;
    function(x);     // 左值
    function(std::move(x));    // 右值
}

此时编译后输出结果为:

lvalue = 10
lvalue = 10

为什么会出现这样的情况?是因为param的确依据传入的参数来确定其左值还是右值,但是在函数内部传给其它函数的时候又会被当作左值传入。这个时候完美转发就来解决这个问题,完美转发可以依据模板T的类型来强制将函数内部的参数转换为对应的类型。重新改写如下:

#include <iostream>
using namespace std;

// 接收左值
void fun(int& lvalue)
{
    std::cout << "lvalue = " << lvalue << std::endl; 
}
// 接收右值
void fun(int&& rvalue)
{
    std::cout << "rvalue = " << rvalue << std::endl; 
}
// 万能引用
template<typename T>
void function(T&& param)
{
    //fun(param);
    fun(std::forward<T>(param)); // 使用std::forward进行完美转发
}

int main()
{
    int x = 10;
    function(x);     // 左值
    function(std::move(x));    // 右值
}

此时,结果输出为:

lvalue = 10
rvalue = 10

6. 参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值