C++11之右值引用和万能引用

本文介绍了C++中的左值和右值概念,强调了右值引用与左值引用的区别,以及如何通过模板完美转发解决函数参数问题。重点讨论了C++11后引入的万能引用和模板特化的应用。
  1. 右值引用

. 左值与右值

首先简明扼要地介绍下左值和右值



右值:只能在=右边的值。
左值:可以在=左边的值。

右值不能在=左边。
左值也可以在=右边。

例如:

int n = 1;

string s = "Hello World";

const float PI = 3.1415926535;

const float PI_2 = PI * 2;

string t = string("abc");

上面这些

n,s,PI,PI_2、t都是左值;

1、"Hello World"、3.1415926535、PI * 2,string("abc")这些都是右值。

说明

字符串字面量的右值身份存在争议,因为它可以取地址,例如:&(“abc”),但它是货真价实的右值。

区别:

No. 分类 左值 右值

1 生存周期 语句外(语句结束还存在) 语句内(语句结束就不存在的)

2 操作 可以取地址 不可以取地址(字符串字面量除外)

2. 左值引用和右值引用

在C++11之前,没有左值引用与右值引用之分,引用专指左值引用。

如果不做区分,就会有以下这种情况出现:

#include <iostream>

using namespace std;

void Print(string& s){

    cout << s;

}

int main(){

    string s="abc";

    Print(s); // OK

    Print("abc"); // parse error

}

自定义对象也会出现这种情况

问题就发生在:d是左值(有名对象),Demo()是右值(匿名对象/临时对象)。

在之前的编码过程中我们很有可能几乎都没遇到过这种问题,因为C++对const&添加特殊技能:既能接受左值又能接受右值。上面两个例子把函数参数加上const,即void Print(const string& s)和void Func(const Demo &),问题就解决了。

void Func(const Demo &)类型匹配解决了,为什么void Print(const string& s)也可以了呢?因为编译器自动把根据函数参数类型string,自动为"abc"构造一个临时对象string("abc"),这是默认类型转换。

3. 完美转发

上面虽然把问题解决了,但是你会发现为了解决右值问题,每个函数都要写两边,并且函数名和函数体都一样,只是参数类型有区分,一个是左值,一个是右值。问题变得更加麻烦了。于是,在模板语法上添加了一个完美转发的语法。(C++滚雪球式添加新的语法,导致C++愈来愈复杂。)

上面两个Login()函数可以合并成

template<class T>

bool Login(T&& user) {

    // 此处略去验证

    user.count++;

    return true

}

注意,这里的&&已经不是右值引用了,而是被称为万能引用(universal references),而这种用法称为完美转发。

当传入右值时,模板特化成bool Login(User&& user),当传入左值时,模板特化成bool Login(User& user)。瞬间解决上面的问题。

&&在模板参数中表示万能引用,在非模板参数中表示右值引用。

<think>我们刚刚讨论过堆栈的区别,现在用户转向C++11引用lambda表达式。根据用户要求,我需要提供这两部分的详细介绍。 注意:用户提到参考了站内引用[1],其中简要提到引用lambda,但内容有限。因此,我将基于C++11标准全面介绍这两个特性。 计划回答结构: 1. 引用(Rvalue References) - 左的概念 - 引用的定义与作用(移动语义、完美转发) - 移动构造函数移动赋运算符 - 示例代码 2. Lambda表达式 - 基本语法 - 捕获列表(捕获、引用捕获、隐式捕获等) - 可变lambda (mutable) - 返回类型推断 - 示例代码 3. 相关问题(按用户要求生成) 注意:用户要求使用LaTeX格式的数学公式(虽然此主题可能较少涉及公式,但如果有机会就使用),并且使用中文回答。 </think>### C++11 引用与 Lambda 表达式详解 #### 一、引用(Rvalue References) ##### 1. 核心概念 - **左 (lvalue)**:持久存在的对象(如变量、函数返回),可被取地址 ```cpp int x = 10; // x 是左 int* p = &x; // 可获取地址 ``` - ** (rvalue)**:临时对象(如字面量、表达式结果),生命周期短暂 ```cpp int sum = x + 5; // (x+5) 是 ``` - **引用 (&&)**:绑定到引用,语法为 `T&&` ```cpp int&& rref = 10; // 引用绑定字面量 ``` ##### 2. 核心作用:移动语义 - **场景**:避免深拷贝大型对象(如 `std::vector`) - **传统问题**(拷贝构造): ```cpp std::vector<int> createVector() { return std::vector<int>(1000000, 1); // 返回临时vector } std::vector<int> v = createVector(); // 触发拷贝构造(深拷贝百万元素)[^1] ``` - **解决方案**(移动构造): ```cpp class Vector { public: // 移动构造函数 Vector(Vector&& other) noexcept : data_(other.data_), size_(other.size_) { other.data_ = nullptr; // 源对象置空,避免重复释放 } private: int* data_; size_t size_; }; ``` **效果**:资源所有权转移(浅拷贝),时间复杂度从 $O(n)$ 降为 $O(1)$[^1]。 ##### 3. 完美转发 (Perfect Forwarding) - **目标**:保持参数原始类型(左/)传递给下层函数 - **实现**:`std::forward<T>` + 模板推导 ```cpp template<typename T> void wrapper(T&& arg) { // 万能引用 process(std::forward<T>(arg)); // 转发原始类型 } ``` #### 二、Lambda 表达式 ##### 1. 基本语法 ```cpp [capture](parameters) mutable -> return_type { // 函数体 } ``` - **示例**: ```cpp auto square = [](int x) -> int { return x*x; }; std::cout << square(5); // 输出 25 ``` ##### 2. 捕获列表(Capture List) | 捕获方式 | 语法 | 效果 | |----------------|---------------|------------------------------| | 捕获 | `[x]` | 创建局部副本 | | 引用捕获 | `[&x]` | 直接引用外部变量 | | 隐式捕获 | `[=]` | 捕获所有外部变量() | | 隐式引用捕获 | `[&]` | 捕获所有外部变量(引用) | | 混合捕获 | `[&, x]` | x捕获,其余引用捕获 | **示例**: ```cpp int a = 1, b = 2; auto lambda = [a, &b]() mutable { a++; // 修改副本(需mutable) b++; // 修改原始变量 }; lambda(); // 执行后:a=1(外部不变),b=3 ``` ##### 3. 应用场景 - **STL 算法**(如 `std::sort`): ```cpp std::vector<int> nums = {3, 1, 4, 2}; std::sort(nums.begin(), nums.end(), [](int a, int b) { return a > b; // 降序排序 }); ``` - **异步编程**(结合 `std::async`): ```cpp auto future = std::async([](int x) { return x*x; }, 10); std::cout << future.get(); // 输出 100 ``` #### 三、引用与 Lambda 的协同 ```cpp std::vector<std::unique_ptr<Resource>> resources; // 使用引用捕获移动资源 auto addResource = [&res = resources](std::unique_ptr<Resource>&& r) { res.push_back(std::move(r)); // 移动而非拷贝 }; addResource(std::make_unique<Resource>()); // 传递 ``` --- ### 关键差异总结 | **特性** | **引用** | **Lambda 表达式** | |----------------|--------------------------------------|-------------------------------------| | **主要目的** | 优化资源转移(移动语义) | 创建匿名函数对象 | | **语法标志** | `T&&` | `[](){}` | | **核心机制** | 转移对象所有权 | 闭包捕获外部变量 | | **典型应用** | 移动构造、完美转发 | STL算法回调、异步任务 | | **性能影响** | 减少拷贝开销 | 可能引入捕获开销(捕获时) | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程经验随笔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值