"Hello World" Lambda Example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | #include <iostream> #include <string> #include <vector> #include <functional> #include <algorithm> class LambdaMutable { // this is a function object private: int id; // copy from outside public: LambdaMutable(int _id) : id(_id) {} void operator() () { std::cout << "id: " << id << std::endl; id++; } }; void BinderExample() { auto plus10times = std::bind(std::multiplies<int>(), std::bind(std::plus<int>(), std::placeholders::_1, 10), 2); auto plus10time2ByLambda = [](int i) { return (i + 10) * 2; }; std::cout << "BinderExample: " << "binder=" << plus10times(1) << " lambda=" << plus10time2ByLambda(1) << std::endl; } /*** Type of Lambdas ***/ std::function<int(int, int)> returnLambda() { return[](int x, int y) { return x * y; }; } int main() { /*** Hello world ***/ [] { std::cout << "Hello, world! (Directly call)" << std::endl; }(); auto lambdaPrintHelloWorld = [] { std::cout << "Hello, world! (Call by auto)" << std::endl; }; lambdaPrintHelloWorld(); /*** Parameter ***/ auto lambdaPrintString = [](const std::string& str) { std::cout << str << std::endl; }; lambdaPrintString("Hello lambda!"); /*** Return type ***/ [] { // no return type, it is deduce by return value, this case is int return 42; }; []() -> double { return 42; }; std::vector<int> vec = { 1, -2, 3, -4, 5 }; auto easyPrint = [](int i) { std::cout << "vec contains " << i << std::endl; }; std::transform(vec.begin(), vec.end(), vec.begin(), [](int i) { return i < 0 ? -i : i; }); // return type deduced. std::for_each(vec.begin(), vec.end(), easyPrint); std::transform(vec.begin(), vec.end(), vec.begin(), [](int i) { if (i < 0) return i; else return -i; }); // return type deduced. std::for_each(vec.begin(), vec.end(), easyPrint); /*** Capture ***/ int x = 0; int y = 123; auto lambdaCap = [x, &y] { //x = 10; // compiler error y++; // ok! std::cout << "x: " << x << std::endl; std::cout << "y: " << y << std::endl; }; x = y = 456; lambdaCap(); lambdaCap(); auto lambdaCap2 = [=, &y] { //x = 10; // compiler error y++; // ok! std::cout << "x: " << x << std::endl; std::cout << "y: " << y << std::endl; }; /*** Mutable ***/ int id = 0; auto lambdaMutable = [id]() mutable { std::cout << "id: " << id << std::endl; id++;// ok! }; id = 123; lambdaMutable(); // print "id: 0" LambdaMutable obj(id); id = 456; obj(); // print "id: 123" /*** Type of Lambdas ***/ auto returnLambdaFuc = returnLambda(); std::cout << returnLambdaFuc(5, 6) << std::endl; /*** Binder v.s. Lambda ***/ BinderExample(); } |
Lambda introducer
Lambda introducer: 左右方框 [] ,稱作 capture- 用來在 lambda 內部存取外部 non-static 物件
- static 物件,例如 std::cout,可直接使用
Lambda Optional
- parameter
- mutable
- exception specification
- attribute specifiers
- return type (trailing return type)
若其中一項出現,則需強制加上 parameter 的左右括號 ( )
[...] (...) mutable throwSpec -> retType {...}
若沒指定 return type,lambda 會自動從回傳值 deduce
Capture(access to outer scope)
Implicitly Capture
- [=] 外部 scope 透過 pass-by-value 傳入 lambda,外部物件在 lambda 內為 read-only
- 採用 capture by value 的前提是外部物件有支援拷貝,例如 ostream 不支援拷貝,僅能使用 capture by reference
- capture 的外部物件會在 lambda 創建時拷貝,並非呼叫時拷貝
- [&] 外部 scope 透過 pass-by-reference 傳入 lambda,外部物件在 lambda 內為 read-write
- 採用 capture by reference,必須確保 lambda 執行時外部物件仍是存在的
Explicitly Capture
- 指定傳入的物件與方式,例如,[x, &y]
Both
- 搭配混用,例如 [=, &y, &z]
- [&, identifier_list],identifier_list 內只可用 =
- [=, identifier_list],identifier_list 內只可用 &
- 必須 = 或 & 開頭,作為 default capture 行為
- 必須交叉使用,例如 [&, =x] 或 [=, &os]
Mutable
- [=] or [x] 變成 read-write
Lambda 行為
可比擬為一個具有以下項目的 class:
- private data member: 與 capture 傳入的 Object 相同
- overload operator ()
- 若有宣告 mutable,則 operator() 會是 non-const member function,否則是 const member function
Lambda 型別
- 一個 anonymous function object or functor,且每個 lambda 的型別是唯一的
- 因為唯一性,宣告 type 必須用 std::function template 或 auto
- 若需要此 type,可以使用 decltype() 取得
auto cmp = [](const Person& p1, const Person &p2) {
return (p1.lastname() < p2.lastname()) ||
(p1.lastname() == p2.lastname() && p1.firstname() < p2.firstname());
};
std::set<Person, decltype(cmp)> coll(cmp);
// decltype(cmp) is type of lambda
// cmp is sorting criteria
- Functional programming 情況下,可使用 std::function<> 定義一個 general type
其他
- 作為 algorithm 的 predicate 或 sorting criterion
auto l = [=](int i) {
return i > x && i < y;
};
auto pos = std::find_if(coll.cbegin(), coll.cend(), l);
- Lambda 不擁有 internal state,需透過 capture 與 pass-by-reference 搭配外部 scope 物件做到
- 與 binder 比較:
auto plus10times = std::bind(std::multiplies<int>(),
std::bind(std::plus<int>(),
std::placeholders::_1,
10),
2);
auto plus10time2ByLambda = [](int i) {
return (i + 10) * 2;
};
- Lambda 不可以有 default argument
- Lambda 不可以為 template
Reference
- The C++ Standard Library: A Tutorial and Reference
- C++ Primer
沒有留言:
張貼留言