「生活可以更简单, 欢迎来到我的开源世界」
  1. 用法示例
  2. 记录相关
    1. lazy evaluation (惰性求值)
    2. 斐波那契lambda写法
    3. auto类型的参数
  3. lambda捕获
lambda 表达式
2020-12-07
C++

C++是唯一一个lambda用完所有括号的语言

用法示例

lambda 表达式(通常称为 “ lambda“)是一种在被调用的位置或作为自变量传递给函数的位置定义匿名函数对象(闭包)的简便方法。 Lambda 通常用于封装传递给算法或异步方法的少量代码行。

#include <iostream>
#include <algorithm> // sort函数模板、for_each函数模板
#include <vector>

using namespace std;


//[capture list](parameter list)-> return type {function body}
int main ( )
{
auto f1 = []() { cout << "lambda test" << endl; };
f1();

//use paramter list
auto f2 = [](int a, int b) { cout << a << ' ' << b << endl; };
f2(3, 4);

//use capture list
int a = 1;
auto f3 = [a]() { cout << a << endl; }; //值捕获
f3();
auto f4 = [&a]() { a++; cout << a << endl; };//引用捕获
f4();
auto f5 = [&]() { a++; cout << a << endl; };//隐式捕获引用
f5();
auto f6 = [=]() { cout << a << endl; };//隐式捕获值
f6();
auto f7 = [=]() mutable { a++; cout << a << endl; }; //隐式捕获值,可变lambda,捕获的值在函数体改变不影响外面变量
f7();
cout << "after mutable" << a << endl;
auto f8 = [&,a]() { cout << a << endl; };
f8();
auto f9 = [=, &a]() { a++; cout << a << endl; };
f9();

//return type
//函数体包含除return之外的任何语句,编译器默认此lambda返回void,可手动声明返回类型
vector<int> v1 = {1, 3, -2, -4, 5, -6};
//下面按照标准是不能通过编译的,但是编译器实现了类型推断(可能是实现了C++14)
//lambda返回auto,auto推断成int,推断成功的前提是返回的都是相同类型
//https://stackoverflow.com/questions/26692637/c11-restrictions-on-lambda-return-type
transform(v1.begin(), v1.end(), v1.begin(), [](int a) {if(a > 0)return a; else return -a; });
//标准用法:
transform(v1.begin(), v1.end(), v1.begin(), [](int a) -> int {if(a > 0)return a; else return -a; });
for_each(v1.begin(), v1.end(), [](int a) { cout << a; });
//error :return type 'char' must match previous return type 'int' when lambda expression has unspecified explicit return type
//transform(v1.begin(), v1.end(), v1.begin(), [](int a) {if(a > 0)return a; else return 'a'; });
return 0;
}

记录相关

lazy evaluation (惰性求值)

trpl:可以创建一个存放闭包和调用闭包结果的结构体。该结构体只会在需要结果时执行闭包,并会缓存结果值,这样余下的代码就不必再负责保存结果并可以复用该值。你可能见过这种模式被称 memoizationlazy evaluation (惰性求值)

#include <iostream>
#include <functional>
#include <memory>
using namespace std;

template<typename T>
class Lazy
{
public:
Lazy() {}

//保存需要延迟执行的函数
template<typename Func, typename ...Args>
Lazy(Func& f, Args&& ...args)
{
//两种写法
//m_function = [&f, &args...]() { return f(std::forward<Args>(args)...); };
m_function = std::bind(f, std::forward<Args>(args)...);
}

//延迟执行,将结果保存起来
T& value()
{
if (!m_isCreate)
{
m_result = m_function();
m_isCreate = true;
}
return m_result;
}

private:
std::function<T()> m_function;
T m_result;
bool m_isCreate = false;
};

//帮助函数 -> 将要执行的函数以及函数的入参保存成Lazy对象
template<class Func, typename ...Args>
Lazy<typename std::result_of<Func(Args...)>::type>
lazy(Func&& f, Args&& ...args)
{
return Lazy<typename std::result_of<Func(Args...)>::type>(
std::forward<Func>(f), std::forward<Args>(args)...);
}

struct Object //big object
{
public:
Object()
{
cout << "big object create" << endl;
}
};

struct MyStruct //operation struct
{
MyStruct()
{
auto fun = [] {return std::make_shared<Object>(); };
m_obj = lazy(fun);
}

void load()
{
m_obj.value();
}

Lazy<std::shared_ptr<Object>> m_obj;
};

int foo(int x)
{
return x * 20;
}

int main()
{
//测试大对象延迟加载
MyStruct t;
t.load();

//测试普通函数加载延迟加载
auto lazyer1 = lazy(foo, 4);
cout << lazyer1.value() << endl;

//测试无参数lamda
auto lazy_func2 = []() {return 12; };
Lazy<int> lazyer2 = lazy(lazy_func2);
cout << lazyer2.value() << endl;

//测试有参数lamda
auto lazy_func3 = [](int x) {return 10 + x; };
Lazy<int> lazyer3 = lazy(lazy_func3, 20);
cout << lazyer3.value() << endl;

//测试有参数的function
std::function<int(int)> f = [](int x) {return x + 3; };
auto lazyer4 = lazy(f, 4);
cout << lazyer4.value() << endl;

system("pause");
return 0;
}

斐波那契lambda写法

#include <iostream>
#include <functional>

int main()
{
//必须捕获引用,按值捕获会产生复制,如果按值捕获,就是”先有鸡还是先有蛋“问题了
//闭包存在的价值是立即捕获,对于lazy类型(惰性求值)也可用捕获引用
std::function<int(int)> f = [&f](int i){
if(i < 2) return i;
return f(i - 1) + f(i - 2);
};

std::cout << f(10) << std::endl;

std::cout << [](int i){
auto f = [&](auto f, int n){
if(n < 2) return n;
return f(f, n - 1) + f(f, n - 2);
};
return f(f, i);
}(10);

return 0;
}

auto类型的参数

#include <iostream>
#include <functional>

int main()
{
//lambda对auto类型的参数可识别出其类型
auto fn = [](auto val){
std::cout << typeid(val).name() << std::endl;
};

fn(123);
fn(123.f);
fn("123");

return 0;
}

lambda捕获

声明捕获的几种形式:

lambda-capture:

capture:

  • simple-capture
    • identifier
    • &identifier
    • this:捕获this意味着通过引用捕获*this(它是对象的左值),而不是通过值捕获指针
    • *this:捕获本地实体的值
  • init-capture
    • identifier initializer
    • &identifier initializer

参考:https://stackoverflow.com/questions/16323032/why-cant-i-capture-this-by-reference-this-in-lambda

<⇧>