C++20 Lambda表达式捕获模版不定实参(Lambda capture of parameter pack)

本文介绍C++20中Lambda表达式的增强特性,包括如何捕获模板不定长实参,支持传值和引用捕获,展示了C++20在函数式编程方面的进步。

C++20之前Lambda表达式只能捕获固定实参,如:

template <typename T>
auto fun(T&& t){
    return [t = std::forward<T>(t)] {
    };
}

C++20现在可以捕获模版不定长实参了:

template <typename... Args>
auto funs(Args&&... args){
    // 传值捕获
    return [...args = std::forward<Args>(args)] {

    };
}

也可以捕获引用:

template <typename... Args>
auto funs_reference(Args&&... args){
    // 引用捕获
    return [&...args = std::forward<Args>(args)] {

    };
}

 

<think> 我们正在讨论C++中的Lambda表达式如何传递形参和实参。 根据引用[1]和[2]提供的背景,我们知道Lambda表达式C++11引入的匿名函数,可以方便地创建函数对象。 引用[3]则涉及了Lambda表达式中的参数传递和完美转发(forward)技术。 在C++中,Lambda表达式的形参列表定义在方括号[]之后的圆括号()内,而实参则在调用时传递。 具体步骤: 1. Lambda表达式的基本语法: [capture](parameters) mutable -> return-type { body } 2. 形参(parameters): - 类似于普通函数的形参,用于定义函数调用时需要传递的参数。 - 可以指定默认参数(C++14起支持)。 - 可以使用auto作为参数类型(C++14起),形成泛型Lambda。 3. 实参(arguments): - 在调用Lambda表达式时,需要传递与形参类型和数量匹配的实参。 - 可以传递左值、右值,也可以通过引用传递。 4.捕获和引用捕获(与形参无关,但影响Lambda内部对捕获变量的使用): - [=]:以值捕获所有外部变量 - [&]:以引用捕获所有外部变量 - 也可以显式指定捕获的变量,如[a, &b] 5. 完美转发: - 引用[3]提到,如果需要在Lambda表达式中保持参数的左值/右值属性,可以使用std::forward进行完美转发。 - 需要将Lambda表达式定义为可变(mutable),并且使用auto&&或模板参数(C++20起支持显式模板参数)来接收参数。 示例代码: 示例1:基本参数传递 ```cpp #include <iostream> int main() { // 定义一个Lambda,接受两个int参数 auto add = [](int a, int b) -> int { return a + b; }; std::cout << add(3, 4) << std::endl; // 输出7,传递实参3和4 } ``` 示例2:使用默认参数(C++14起) ```cpp #include <iostream> int main() { // 默认参数为5 auto multiply = [](int a, int b = 5) -> int { return a * b; }; std::cout << multiply(3) << std::endl; // 输出15 (3*5) std::cout << multiply(3, 10) << std::endl; // 输出30 (3*10) } ``` 示例3:使用auto参数(C++14起,泛型Lambda) ```cpp #include <iostream> int main() { // 使用auto参数,可以接受不同类型的参数 auto print = [](auto value) { std::cout << value << std::endl; }; print(42); // 整数 print(3.14); // 浮点数 print("Hello"); // 字符串 } ``` 示例4:完美转发(保持参数的值类别) 引用[3]中提到了forward的用法,在Lambda中实现完美转发需要将Lambda定义为可变(mutable)并使用auto&&(通用引用): ```cpp #include <iostream> #include <utility> void process(int& x) { std::cout << "lvalue: " << x << std::endl; } void process(int&& x) { std::cout << "rvalue: " << x << std::endl; } int main() { auto wrapper = [](auto&& arg) { process(std::forward<decltype(arg)>(arg)); }; int x = 42; wrapper(x); // 传递左值,输出 lvalue: 42 wrapper(123); // 传递右值,输出 rvalue: 123 return 0; } ``` 注意:这里使用了`auto&&`来捕获参数,这样arg可以成为通用引用(既能绑定左值也能绑定右值)。然后使用`std::forward<decltype(arg)>`来转发参数,保持其原始的值类别。 关于捕获列表: - 捕获列表用于捕获外部变量,使得Lambda内部可以使用这些变量。 - 捕获方式分为值捕获和引用捕获。 - 捕获和参数传递是两个不同的机制:捕获是在创建Lambda捕获外部变量,而参数传递是在调用Lambda时传递实参。 示例5:捕获与参数结合 ```cpp #include <iostream> int main() { int base = 100; // 值捕获base,并接受一个int参数 auto add_base = [base](int x) -> int { return base + x; }; std::cout << add_base(5) << std::endl; // 输出105 // 引用捕获base auto add_base_ref = [&base](int x) -> int { base += x; return base; }; std::cout << add_base_ref(5) << std::endl; // 输出105(base变为105) std::cout << base << std::endl; // 输出105,因为引用捕获改变了base的值 } ``` 注意事项: 1.Lambda表达式中,如果使用引用捕获,需要注意被引用变量的生命周期,避免悬垂引用。 2. 默认情况下,Lambda表达式是一个const函数对象,即不能修改值捕获的变量(除非使用mutable关键字)。 3. 完美转发在泛型Lambda中非常有用,尤其是在编转发函数时(如包装器)。 引用[4]提到了实参类型(位置参数和关键字参数)的概念,但在C++中并不区分位置参数和关键字参数,因为C++函数调用严格依赖参数位置和类型。在Lambda表达式中,参数传递同样遵循位置顺序。 总结: - 形参:在Lambda的括号()内声明,指定类型(或使用auto)和可选的默认值。 - 实参:调用Lambda时提供,必须与形参的数量和类型兼容(考虑隐式转换)。 因此,在C++中,Lambda表达式的参数传递机制与普通函数基本相同,同时还可以结合捕获列表来使用外部变量。 相关问题: 1. C++ Lambda表达式如何捕获外部变量?值捕获和引用捕获有什么区别? 2.C++ Lambda表达式中使用mutable关键字的作用是什么? 3. 如何实现Lambda表达式的递归调用? 4. C++14和C++20Lambda表达式有哪些改进? </think> ### C++ Lambda表达式参数传递详解 在C++中,Lambda表达式的参数传递机制融合了函数参数传递的特性(形参与实参)和独特的捕获机制。以下是关键概念解析: #### 1. **形参声明与实参传递** Lambda表达式的参数列表定义在`(...)`中,与普通函数类似: ```cpp auto lambda = [](int a, double b) { return a * b; }; // a,b是形参 auto result = lambda(5, 3.14); // 5,3.14是实参 ``` - **位置传递**:实参按声明顺序绑定到形参[^4] - **类型匹配**:要求实参与形参类型兼容(支持隐式转换) - **默认参数**(C++14起支持): ```cpp auto greet = [](string name = "Guest") { cout << "Hello, " << name; }; greet(); // 输出 "Hello, Guest" ``` #### 2. **捕获列表 vs 形参列表** Lambda特有捕获机制(`[]`内)与参数列表(`()`内)的区别: | 特性 | 捕获列表 `[]` | 形参列表 `()` | |--------------|---------------------|---------------------| | 绑定时机 | Lambda创建时 | Lambda调用时 | | 访问范围 | 外部作用域变量 | 调用时传入的值 | | 传递方式 | 值捕获/引用捕获 | 值/引用传递 | | 生命周期影响 | 影响被捕获变量 | 不影响外部实参 | 示例: ```cpp int base = 10; // 捕获base(值捕获),形参x auto adder = [base](int x) { return base + x; }; cout << adder(5); // 输出15(base=10, x=5) ``` #### 3. **值传递与引用传递** - **值传递**(默认): ```cpp auto func = [](int a) { a *= 2; }; int x = 5; func(x); // x仍为5(形参a是副本) ``` - **引用传递**: ```cpp auto modifier = [](int& a) { a *= 2; }; int y = 5; modifier(y); // y变为10(修改原值) ``` #### 4. **完美转发(Perfect Forwarding)** 使用`auto&&`和`std::forward`保持值类别(左值/右值)[^3]: ```cpp auto wrapper = [](auto&& arg) { process(std::forward<decltype(arg)>(arg)); }; wrapper(42); // 转发右值 int z = 10; wrapper(z); // 转发左值 ``` 关键点: 1. `auto&&` 生成转发引用 2. `decltype(arg)` 获取原始类型 3. `std::forward` 保持值类别 #### 5. **泛型LambdaC++14+)** 使用`auto`形参实现泛型: ```cpp auto generic = [](auto a, auto b) { return a + b; }; cout << generic(3, 4.2); // 输出7.2(int+double) ``` #### 6. **可变Lambda(mutable)** 默认Lambda为`const`,需`mutable`修改值捕获的副本: ```cpp int counter = 0; auto counter_func = [counter]() mutable { counter++; // 无mutable会编译错误 return counter; }; ``` ### 关键差异总结 1. **捕获列表**定义Lambda创建时的环境访问权限 2. **形参列表**定义Lambda调用时的输入接口 3. 两者可组合使用,例如同时捕获外部变量并接受调用参数: ```cpp double tax_rate = 0.2; auto calc_tax = [tax_rate](double income) { return income * tax_rate; }; ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值