C++20嚐鮮:Lambda表示式變化

語言特性

提案

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 #include 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;}

C++20嚐鮮:Lambda表示式變化

線上編譯執行

https://wandbox。org/nojs/gcc-headhttps://wandbox。org/nojs/clang-head

通用lambda的模板引數列表

C++20允許使用熟悉的模板函式語法直接引入型別。

#include #include #include // 完美轉發template void print(T &&。。。 t){ (std::cout << 。。。 << t) << std::endl;}int main(){ std::vector ivec = {0, 1, 2, 3, 4, 5}; // lambda 期望 std::vector // 在 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後 [](std::vector vec){ for(auto& v : vec) { std::cout << v << std::endl; } }(ivec); // 使用引數型別 // 在 C++20前 [](const auto& x){ using T = std::decay_t; T copy = x; using Iterator = typename T::const_iterator; Iterator iter = x。cbegin(); std::cout << *iter << std::endl; }(ivec); // 從 C++20後 [](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(args)。。。); }(1, 2。2, ‘c’); // 從 C++20後 [](Ts&&。。。 args){ print(std::forward(args)。。。); }(1, 2。2, ‘c’); // 混合auto和T [](const T& a, auto b){ std::cout << a << b << std::endl; }(1,“hello”); return 0;}

在未計算的上下文中

Lambda表示式可以在未計算的上下文中使用,如sizeof()、typeid()、decltype()等。主要的原則是lambdas有一個唯一的未知型別,兩個lambdas及其型別永遠不相等。

// 以下模板是兩個不同的宣告template void f(decltype([]{}) (*s)[sizeof(T)]);template void f(decltype([]{}) (*s)[sizeof(T)]);

在下面的例子中,f()在兩個翻譯單元中增加同一個計數器,因為行內函數的行為就好像它只有一個定義。然而,g_s違反了ODR,因為儘管它只有一個定義,但仍然有多個不同的宣告,因為在a。cpp和b。cpp中有兩個不同的lambdas,因此,S有不同的非型別模板引數。

a.h

templateint counter(){ static int value{0}; return ++value;}inline int f(){ return counter();}template struct S{ int call(){static int value{0};return ++value;} };// cast lambda to pointerinline S<+[]{}> g_s;

b.cpp

#include #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 #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;}

C++20嚐鮮:Lambda表示式變化

線上執行測試

https://wandbox。org/permlink/0nIwKlQa3hsRp2LJ

執行結果:

f:1g:1f:2g:1

預設的可構造和可賦值的無狀態lambda

在C++20中,無狀態lambda是預設可構造和可賦值的,在未求值上下文中,我們可以透過decltype()獲得lambda的型別,並在稍後建立該型別的變數。

例子中,std::map接受一個比較器型別,以便稍後例項化它。雖然我們可以在C++17中獲得一個lambda型別,但無法例項化它,因為lambdas不是預設可構造的。

#include #include #include int main(){ auto greater = [](auto x,auto y){ std::cout << x << y << std::endl; return x > y; }; // 需要是預設可構造的型別 std::map map1; // 需要是預設可賦值的型別 auto map2 = map1; map2[“1”] = 1; map2[“2”] = 2; map2[“3”] = 3; return 0;}

執行結果:

12212113233232

在lambda捕獲中進行引數包展開

C++20簡化了lambda中的引數包捕獲。在C++20之前,如果我們想移動包,可以透過值、引用或std::tuple來捕獲它們。現在就簡單多了,我們可以在初始化捕獲中直接捕獲引數包。它並不侷限於std::move或std::forward,任何函式都可以應用於引數包元素。

#include #include #include void f(double, double){std::cout << “fd” << std::endl;}void g(int, int,double){std::cout << “gi” << std::endl;}// C++17templateauto delay_apply(F&& f, Args&&。。。 args) { return [f=std::forward(f), tup=std::make_tuple(std::forward(args)。。。)]() -> decltype(auto) { return std::apply(f, tup); };}// C++20templateauto delay_call(F&& f, Args&&。。。 args) { return [f = std::forward(f), 。。。f_args=std::forward(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;}