C++ Lambda表達式第一篇, 閉合Closuretype
- ClosureType::operator()(params)
- auto 模板參數類型
- 顯式模板參數類型
- 其他
- ClosureType::operator ret(*)(params)()
lambda 表達式是唯一的未命名,非聯合,非聚合類類型(稱為閉包類型)的純右值表達式,它在包含 lambda 的最小塊作用域、類作用域或命名空間作用域中聲明(出于 ADL 的目的)表達。
當且僅當 captures 為空時,閉包類型才是結構類型。
閉包類型具有以下成員,它們不能顯式實例化、顯式專門化或在友元聲明中命名:
ClosureType::operator()(params)
ret operator()(params) { body }
template<template-params>
ret operator()(params) { body }
調用時,執行 lambda 表達式的主體。訪問變量時,訪問其捕獲的副本(對于通過副本捕獲的實體)或原始對象(對于通過引用捕獲的實體)。
如果提供了operator()的參數列表,則為params,否則參數列表為空。
operator()的返回類型是trailing-type中指定的類型。
如果未提供 Trailing-type,則自動推導出operator() 的返回類型。
除非在 lambda 說明符中使用關鍵字 mutable,或者存在顯式對象參數,否則 operator() 的 cv 限定符為 const,并且通過 copy 捕獲的對象從內部不可修改這個運算符()。不允許顯式 const 限定符。 operator() 從來都不是虛擬的,并且不能具有 volatile 限定符。
- 如果operator() 滿足constexpr 函數的要求,則它始終是constexpr。如果 lambda 說明符中使用了關鍵字 constexpr,那么,它也是 constexpr。
- 如果 lambda 說明符中使用了關鍵字 consteval,則operator() 是立即函數。
- 如果 lambda 說明符中使用了關鍵字 static,則operator() 是靜態成員函數。
- 如果 params 包含顯式對象參數,則operator() 是顯式對象成員函數。
auto 模板參數類型
對于 params 中類型指定為 auto 的每個參數,都會按照出現的順序將發明的模板參數添加到 template-params 中。如果params對應的函數成員是函數參數包,則本發明的模板參數可以是參數包。
#include <iostream>
#include <iostream>
#include <fstream>using namespace std;// generic lambda, operator() is a template with two parameters
auto glambda = [](auto a, auto&& b) { return a < b; };// generic lambda, operator() is a template with one parameter
auto vglambda = [](auto printer)
{return [=](auto&&... ts) // generic lambda, ts is a parameter pack{ printer(forward<decltype(ts)>(ts)...);// nullary lambda (takes no parameters):return [=] { printer(ts...); };};
};auto p = vglambda([](auto v1, auto v2, auto v3)
{cout << v1 << " " << v2 << " " << v3 << endl;
});int main() { int x = 100;bool b = glambda(3, (x / 10) - 3.14);cout << b << endl;b = glambda(3, (x / 20) - 3.14);cout << b << endl;auto q = p(1, 'a', 3.14); // outputs 1 a 3.14q(); // outputs 1 a 3.14auto pp = vglambda(printf);pp("%s %d \n", "Sam", 45);
}
代碼運行的屏幕輸出
1
0
1 a 3.14
1 a 3.14
Sam 45
顯式模板參數類型
如果 lambda 定義使用顯式模板參數列表,則該模板參數列表將與 operator() 一起使用。對于 params 中類型指定為 auto 的每個參數,一個新的模板參數類型,將作為該模板參數列表的類型,直至到參數列表的末尾:
#include <iostream>
#include <iostream>
#include <fstream>using namespace std;struct A
{A(int&& n) { cout << "rvalue overload, n=" << n << '\n'; }A(int& n) { cout << "lvalue overload, n=" << n << '\n'; }
};class foo
{
public:template<class T1, class T2, class T3>foo(T1&& t1, T2&& t2, T3&& t3) :a1_{forward<T1>(t1)},a2_{forward<T2>(t2)},a3_{forward<T3>(t3)}{}private:A a1_, a2_, a3_;
};// generic lambda, operator() is a template with two parameters
auto glambda = []<class T>(T a, auto&& b) { return a < b; };// generic lambda, operator() is a template with one parameter pack
auto f1 = []<typename... Ts>(Ts&&... ts)
{return foo(forward<Ts>(ts)...);
};int main() { int x = 100;bool b = glambda(3, (x / 10) - 3.14);cout << b << endl;b = glambda(5.0, (x / 20) - 3.14);cout << b << endl;f1(1, 2, 4);
}
1
0
rvalue overload, n=1
rvalue overload, n=2
rvalue overload, n=4
其他
lambda 表達式上的異常規范exception適用于operator()。
為了名稱查找、確定 this 指針的類型和值,以及訪問非靜態類成員,閉包類型的operator() 的主體可以認為是 lambda 表達式的一部分。
struct X
{int x, y;int operator()(int);void f(){// 下面的lambda表達式是成員函數 X::f[=]() -> int{return operator()(this->x + y); // X::operator()(this->x + (*this).y)// this has type X*};}
};
ClosureType::operator ret(*)(params)()
- 無捕獲,非常規Lambda* using F = ret(*)(params); operator F() const noexcept;* using F = ret(*)(params);* constexpr operator F() const noexcept;
- 無捕獲,常規lambda* template<template-params>operator fptr_t<template-params>() const noexcept;* template<template-params>constexpr operator fptr_t<template-params>() const noexcept;
僅當 lambda 表達式的捕獲列表為空時,才會定義此用戶定義的轉換函數。該函數是一個閉合對象的成員函數, 而且具有public, constexpr, 非虛、非顯式 和 const noexcept特征。
如果函數調用運算符是立即函數,則此函數是立即函數。
通用的無捕獲 lambda 具有一個用戶定義的轉換函數模板,它具有與 operator() 相同的新模板參數表。
#include <iostream>using namespace std;void f1(int (*f)(int)) {int x = f(2);cout << "f=" << x << endl;
}void f2(char (*)(int)) {}
void h(int (*h)(int)) { // #1int x = h(3);cout << "h=" << x << endl;
}void h(char (*)(int)) {} // #2auto glambda = [](auto a) { return a; };int& (*fpi)(int*) = [](auto* a) -> auto& { return *a; }; // OKint main()
{f1(glambda); // OK
// auto y = f2(glambda); // error: not convertibleh(glambda); // OK: calls #1 since #2 is not convertibleauto x = glambda(1);cout << "x: " << x << endl;auto y = fpi(&x);cout << "y: " << y << endl;
}
轉換函數的返回值是一個指向具有 C++ 語言鏈接的函數指針,調用該函數時,與在閉包類型的默認構造實例上調用閉包類型的函數調用運算符具有相同的效果。
轉換函數(模板)返回的值是一個指向具有 C++ 語言鏈接的函數的指針,調用該函數時,具有與以下相同的效果:
對于非泛型 lambda,在閉包類型的默認構造實例上調用閉包類型的operator()。
對于泛型 lambda,在閉包類型的默認構造實例上調用泛型 lambda 相應的operator() 特化。
轉換函數(模板)返回的值為
如果operator()是靜態的,則為具有C++語言鏈接的指向該operator()的指針,
否則,指向具有 C++ 語言鏈接的函數的指針,在調用該函數時,具有與以下相同的效果:
對于非泛型 lambda,在閉包類型的默認構造實例上調用閉包類型的operator()。
對于泛型 lambda,在閉包類型的默認構造實例上調用泛型 lambda 相應的operator() 特化。
如果函數調用運算符為 constexpr,則此函數為 constexpr。
#include <iostream>using namespace std;auto Fwd = [](int(*fp)(int), auto a) { return fp(a); };
auto C = [](auto a) { return a; };auto NC = [](auto a) { static int s; return a; };int main() { static_assert(Fwd(C, 3) == 3);// static_assert(Fwd(NC, 3) == 3); // error: no specialization can be constexpr because of static s
}