lambda原理篇
demo
int main()
{
int inc = 10;
auto lambda = [&inc](int i) -> int{
return i + inc;
};
std::cout << lambda(1) << std::endl;
}
使用规则
语法:
[捕获参数](参数) mutable -> 返回 {
函数体
};
1.1 捕获参数
捕获参数:捕获的外部变量,用于函数体内访问外部变量;不指定,则不捕获任何外部变量
捕获方式:
&变量名 | 以引用方式捕获 |
变量名 | 已拷贝方式捕获 |
& | 以引用方式捕获当前环境下的所有外部变量 |
= | 以拷贝方式捕获当前环境下的所有外部变量 |
注意点:
1、类中this的捕获方式,[this]、[=]、[&]均是以引用方式捕获this;
2、如果需要以拷贝方式捕获this,[*this]
3、以引用方式捕获数据时,需确保捕获的数据在lambda使用时未被释放,特别是表达式跨函数栈传输时
1.2 mutable
mutable用于确认捕获的参数时候可以在lambda内部修改
有捕获原理分析
拿demo来进行分析,还原demo编译后的内容
int main()
{
int inc = 10;
class __lambda_7_19
{
public:
inline /*constexpr */ int operator()(int i) const
{
return i + inc;
}
private:
int &inc;
public:
__lambda_7_19(int & _inc)
: inc{ _inc }
{}
};
__lambda_7_19 lambda = __lambda_7_19{ inc };
std::cout.operator<<(lambda.operator()(1)).operator<<(std::endl);
return 0;
}
对上述代码进行格式化后:
int main()
{
class __lambda_7_19
{
public:
// 重载运算符(),const代表无法修改成员变量inc
inline int operator()(int i) const
{
return i + inc;
}
// 构造函数
__lambda_7_19(int & _inc)
: inc{ _inc }
{}
private:
// 以引用的方式和外部变量inc共用同一块内存
int & inc;
};
int inc = 10;
// 类对象初始化
__lambda_7_19 lambda = __lambda_7_19{ inc };
std::cout.operator<<(lambda.operator()(1)).operator<<(std::endl);
return 0;
}
从上述内容可以看出一下几点:
1、lambda表达式会转换为类对象
2、捕获的变量会变成类对象的成员变量
3、产生的类中有operator()的重载运算符,lambda表达式的参数为重载运算符()的参数,返回值为重载运算符()的返回值
无捕获原理分析
无捕获的会相对复杂一点,无捕获的会涉及到operator的第二种用法
1、operator重载运算符
2、operator用于隐式转换,比如operator int(),代表可隐式转换为int类型
class Data {
public:
Data(int data):innerData{data}{};
operator int (){
return innerData;
};
private:
int innerData;
}
无捕获举例:
#include <iostream>
int main()
{
auto doubleLambda = [](int i) -> int {
return i + i;
};
doubleLambda(10);
}
还原为编译后的内容:
#include <iostream>
int main()
{
class __lambda_5_25
{
public:
inline /*constexpr */ int operator()(int i) const
{
return i + i;
}
using retType_5_25 = auto (*)(int) -> int;
inline constexpr operator retType_5_25 () const noexcept
{
return __invoke;
};
private:
static inline /*constexpr */ int __invoke(int i)
{
return __lambda_5_25{}.operator()(i);
}
};
__lambda_5_25 doubleLambda = __lambda_5_25{};
doubleLambda.operator()(10);
return 0;
}
格式化后:
#include <iostream>
int main()
{
class __lambda_5_25
{
public:
// 重载()运算符
inline int operator()(int i) const
{
return i + i;
}
// 声明函数指针
using retType_5_25 = auto (*)(int) -> int;
// 这里就比较有意思了,operator的第二种用法,隐式转换,operator retType_5_25 (),隐式转换为函数指针
inline operator retType_5_25 () const
{
return __invoke;
};
private:
// 用私有静态成员函数,作为函数指针
static inline int __invoke(int i)
{
// 调用对象的operator ()重载的运算符
return __lambda_5_25{}.operator()(i);
}
};
__lambda_5_25 doubleLambda = __lambda_5_25{};
doubleLambda.operator()(10);
return 0;
}
总结:
1、无捕获的表达式会生成隐式转换为”函数指针“的函数,用私有静态函数做承接,并通过生成对象、调用operator()重载运算符内容来完成lambda表达式函数体的调用
问题:是否可以为有捕获的lambda表达式生成隐式转换函数和静态函数?为什么?
答案:不行,因为静态函数在生成类对象时,无法指导捕获的内容,也就无法使用捕获的内容来构造类对象
lambda和函数指针的区别
从前面无捕获的lambda表达式可以看出,无捕获的lambda表达式有转换为函数指针的隐式转换函数,因此
无捕获的lambda表达式可转换为函数指针
有捕获的lambda表达式无法转换为函数指针,因为有捕获的必须传递this指针,导致无法转换
举例说明:
typedef int(*DoubleFunType)(int);
void doAction(DoubleFunType fun)
{
fun(10);
}
int main()
{
auto doubleLambda = [](int i) -> int {
return i + i;
};
doAction(doubleLambda);
}
还原编译后的内容:
typedef int(*DoubleFunType)(int);
void doAction(DoubleFunType fun)
{
fun(10);
}
int main()
{
class __lambda_12_25
{
public:
inline /*constexpr */ int operator()(int i) const
{
return i + i;
}
using retType_12_25 = auto (*)(int) -> int;
inline constexpr operator retType_12_25 () const noexcept
{
return __invoke;
}
private:
static inline /*constexpr */ int __invoke(int i)
{
return __lambda_12_25{}.operator()(i);
}
};
__lambda_12_25 doubleLambda = __lambda_12_25{};
doAction(doubleLambda.operator __lambda_12_25::retType_12_25());
return 0;
}
格式化后:
// 声明函数指针
typedef int(*DoubleFunType)(int);
void doAction(DoubleFunType fun)
{
// 调用函数指针
fun(10);
}
int main()
{
// 此处和上面的完全一致
class __lambda_12_25
{
public:
inline /*constexpr */ int operator()(int i) const
{
return i + i;
}
using retType_12_25 = auto (*)(int) -> int;
inline constexpr operator retType_12_25 () const noexcept
{
return __invoke;
}
private:
static inline /*constexpr */ int __invoke(int i)
{
return __lambda_12_25{}.operator()(i);
}
};
__lambda_12_25 doubleLambda = __lambda_12_25{};
// doubleLambda.operator retType_12_25() 强制调用隐式转换为函数指针,而__lambda_12_25::是域作用符,用于标识retType_12_25类型是类内部类型
doAction(doubleLambda.operator __lambda_12_25::retType_12_25());
return 0;
}
总结:
无捕获的lambda可传递给函数指针。
lambda结合std::function构建轻量级代理
结合业务来说,直接用lambda表达式用于传递函数指针,用处不大,因为无法用有捕获的表达式传给函数指针,无捕获的表达式用处有限
而c++提供一个对象std::function,构造函数可以接收lambda表达式和函数指针,没有转换函数指针的限制,为什么呢?
拿刚刚的例子做下简单修改,把函数指针更改为std::function
typedef std::function<int(int)> DoubleFunType;
void doAction(DoubleFunType fun)
{
fun(10);
}
int main()
{
int time = 10;
auto timeLambda = [time](int i) -> int {
return time * i;
};
doAction(timeLambda);
}
还原后:
#include <iostream>
#include <functional>
typedef std::function<int(int)> DoubleFunType;
void doAction(std::function<int (int)> fun)
{
// 关键点在这里,std::function对象的执行是只要支持operator()运算符即可,不不是限制于函数指针
fun.operator()(10);
}
int main()
{
int time = 10;
class __lambda_14_23
{
public:
inline /*constexpr */ int operator()(int i) const
{
return time * i;
}
private:
int time;
public:
// inline /*constexpr */ __lambda_14_23(const __lambda_14_23 &) noexcept = default;
__lambda_14_23(int & _time)
: time{_time}
{}
};
__lambda_14_23 timeLambda = __lambda_14_23{time};
doAction(std::function<int (int)>(timeLambda));
return 0;
}
从注释中可以看出关键点:
std::function对象的执行是只要支持operator()运算符即可,不不是限制于函数指针
因此std::function可以传入带捕获的lambda表达式,从而实现轻量级代理
轻量级代理的设计实现:
https://bbs.huaweicloud.com/blogs/416963
- 点赞
- 收藏
- 关注作者
评论(0)