语言特性 | 提案 |
Allow lambda capture [=, this] | P0409R2 |
Familiar template syntax for generic lambdas | P0428R2 |
Simplifying implicit lambda capture | P0588R1 |
Default constructible and assignable stateless lambdas | P0624R2 |
Lambdas in unevaluated contexts | P0315R4 |
Allow pack expansion in lambda init-capture | P0780R2 |
P2095R0 | |
Deprecate implicit capture of this via [=] | P0806R2 |
lambda捕获 [=, this]
当隐式捕获时(使用[=]),它总是通过引用捕获。为了消除这种混淆,C++20弃用这种行为,允许更明确的[=,this],但[&]保持不变
捕获 是零或更多捕获符的逗号分隔列表,可选地以 默认捕获符 开始。仅有的默认捕获符是
- &(以引用隐式捕获被使用的自动变量)
- = (以复制隐式捕获被使用的自动变量)
#include <iostream>
#include <type_traits>
struct LT{
void f(){
[=]{
std::cout << typeid(this).name() << this->val << std::endl;
}(); //从 C++20弃用, 传引用捕获 this
}
void g(){
[=, *this]{
std::cout << typeid(this).name() << this->val << std::endl;
}(); // 从 C++17, 传值捕获 this
}
void h(){
[=, this]{
std::cout << typeid(this).name() << this->val << std::endl;
}(); //从 C++20, 传引用捕获 this
}
int val = 1;
};
int main()
{
LT lt;
lt.f();
lt.g();
lt.h();
return 0;
}
https://wandbox.org/nojs/gcc-head
https://wandbox.org/nojs/clang-head
通用lambda的模板参数列表
C++20允许使用熟悉的模板函数语法直接引入类型。
#include <iostream>
#include <type_traits>
#include <vector>
// 完美转发
template <typename... T>
void print(T &&... t)
{
(std::cout << ... << t) << std::endl;
}
int main()
{
std::vector<int> ivec = {0, 1, 2, 3, 4, 5};
// lambda 期望 std::vector<T>
// 在 C++20前
[](auto vec){
using T = typename decltype(vec)::value_type;
for(auto& v : vec) {
T t = v;
std::cout << t << std::endl;
}
}(ivec);
// 从 C++20后
[]<typename T>(std::vector<T> vec){
for(auto& v : vec) {
std::cout << v << std::endl;
}
}(ivec);
// 使用参数类型
// 在 C++20前
[](const auto& x){
using T = std::decay_t<decltype(x)>;
T copy = x;
using Iterator = typename T::const_iterator;
Iterator iter = x.cbegin();
std::cout << *iter << std::endl;
}(ivec);
// 从 C++20后
[]<typename T>(const T& x){
T copy = x;
using Iterator = typename T::const_iterator;
Iterator iter = x.cbegin();
std::cout << *iter << std::endl;
}(ivec);
// 完美转发
// 在 C++20前
[](auto&&... args){
print(std::forward<decltype(args)>(args)...);
}(1, 2.2, 'c');
// 从 C++20后
[]<typename... Ts>(Ts&&... args){
print(std::forward<Ts>(args)...);
}(1, 2.2, 'c');
// 混合auto和T
[]<typename T>(const T& a, auto b){
std::cout << a << b << std::endl;
}(1,"hello");
return 0;
}
在未计算的上下文中
Lambda表达式可以在未计算的上下文中使用,如sizeof()、typeid()、decltype()等。主要的原则是lambdas有一个唯一的未知类型,两个lambdas及其类型永远不相等。
// 以下模板是两个不同的声明
template<class T> void f(decltype([]{}) (*s)[sizeof(T)]);
template<class T> void f(decltype([]{}) (*s)[sizeof(T)]);
在下面的例子中,f()在两个翻译单元中增加同一个计数器,因为内联函数的行为就好像它只有一个定义。然而,g_s违反了ODR,因为尽管它只有一个定义,但仍然有多个不同的声明,因为在a.cpp和b.cpp中有两个不同的lambdas,因此,S有不同的非类型模板参数.
a.h
template<typename T>
int counter(){
static int value{0};
return ++value;
}
inline int f(){
return counter<decltype([]{})>();
}
template<auto> struct S{ int call(){static int value{0};return ++value;} };
// cast lambda to pointer
inline S<+[]{}> g_s;
b.cpp
#include <iostream>
#include "a.h"
void func(){
auto v = f();
std::cout << "f:" << v << std::endl;
int gv = g_s.call();
std::cout << "g:" << gv << std::endl;
}
a.cpp
#include <iostream>
#include "a.h"
void func();
int main()
{
auto v = f();
std::cout << "f:" << v << std::endl;
int gv = g_s.call();
std::cout << "g:" << gv << std::endl;
func();
return 0;
}
https://wandbox.org/permlink/0nIwKlQa3hsRp2LJ
运行结果:
f:1
g:1
f:2
g:1
默认的可构造和可赋值的无状态lambda
在C++20中,无状态lambda是默认可构造和可赋值的,在未求值上下文中,我们可以通过decltype()获得lambda的类型,并在稍后创建该类型的变量。
例子中,std::map接受一个比较器类型,以便稍后实例化它。虽然我们可以在C++17中获得一个lambda类型,但无法实例化它,因为lambdas不是默认可构造的。
#include <iostream>
#include <type_traits>
#include <map>
int main()
{
auto greater = [](auto x,auto y){
std::cout << x << y << std::endl;
return x > y;
};
// 需要是默认可构造的类型
std::map<std::string, int, decltype(greater)> map1;
// 需要是默认可赋值的类型
auto map2 = map1;
map2["1"] = 1;
map2["2"] = 2;
map2["3"] = 3;
return 0;
}
运行结果:
12
21
21
13
23
32
32
在lambda捕获中进行参数包展开
C++20简化了lambda中的参数包捕获。在C++20之前,如果我们想移动包,可以通过值、引用或std::tuple来捕获它们。现在就简单多了,我们可以在初始化捕获中直接捕获参数包。它并不局限于std::move或std::forward,任何函数都可以应用于参数包元素。
#include <iostream>
#include <type_traits>
#include <map>
void f(double, double){std::cout << "fd" << std::endl;}
void g(int, int,double){std::cout << "gi" << std::endl;}
// C++17
template<class F, class... Args>
auto delay_apply(F&& f, Args&&... args) {
return [f=std::forward<F>(f), tup=std::make_tuple(std::forward<Args>(args)...)]()
-> decltype(auto) {
return std::apply(f, tup);
};
}
// C++20
template<typename F, typename... Args>
auto delay_call(F&& f, Args&&... args) {
return [f = std::forward<F>(f), ...f_args=std::forward<Args>(args)]()
-> decltype(auto) {
return f(f_args...);
};
}
int main()
{
delay_apply(f, 1.1, 2.2)();
delay_call(g, 1, 2, 3.3)();
return 0;
}
本文暂时没有评论,来添加一个吧(●'◡'●)