語言特性
提案
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;}
線上編譯執行
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;}
線上執行測試
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
執行結果:
12212113233232
在lambda捕獲中進行引數包展開
C++20簡化了lambda中的引數包捕獲。在C++20之前,如果我們想移動包,可以透過值、引用或std::tuple來捕獲它們。現在就簡單多了,我們可以在初始化捕獲中直接捕獲引數包。它並不侷限於std::move或std::forward,任何函式都可以應用於引數包元素。
#include #include #include