webrtc 中如何“接“ lambda表达式

在webrtc中lambda表达式的运用场景实在太过于广泛了,因此如何接是我们要弄明白.

webrtc 中如何"接" lambda表达式

如何理解lambda表达式?

lambda表达式的形式:
【捕捉列表】(参数列表)->返回值 { 函数体};

那么我们怎么来理解这个lambda表达式?

  1. lambda表达式是一个类,底层是通过仿函数来实现的.
  2. 捕捉列表可以理解为 决定类里面的成员变量.

我们来看个demo.

#include <iostream>
#include <string>
using namespace std;

class A
{
public:
	A()
	{
		cout << "A:()" << std::endl;
	}

	A(const A&)
	{
		cout << "A::(A&) 拷贝构造函数" << endl;
	}

	A(A&&)
	{
		cout << "A::(A&&) 移动构造函数" << endl;
	}

	A operator=(const A&)
	{
		cout << "A operator=(const A&)" << endl;
	}

	A operator=(A&&)
	{
		cout << "A operator=(A&&)" << endl;
	}

	~A()
	{
		cout << "A::~A()析构函数" << endl;
	}
};


int main(int argc,char **argv)
{
	A a;
	auto func = [a] {
	};
	func();
	return 0;
}

我们按值的方式来捕捉A,是否会触发A的拷贝构造函数呢?

在这里插入图片描述
答案是会的,func()能被调用,本质因为,这个仿函数类里面存在有类似:
void operaotr() 类似的重载函数.

在我们编译的时候,编译器就会将lambda表达式转化为对应的仿函数.

(lambda表达式底层也有可能是基于模板来实现,大家可以参考 webrtc里面的FunctionView,挺具有参考意义的).

如何来接lambda表达式

接就是让lambda表达式成员类的成员变量,我们在上面说过 lamabda比倒是是一个类,那么接lambda表达式就容易理解了,直接把它视为一个普通类来接不就可以了。

但是接还是存在一些问题的.

比如:
int a = 100 ;
auto func = [] {};

int a; int 是 a的类型 ,但是 lambda表达式 func 只能用auto来表示,类型没有办法具体化,因为lambda表示的编译规则没有指定类名.

其实编译器是知道lambda的具体类型的,比如:
在这里插入图片描述
那么我们可以使用模板参数来接.

我们来以官方的一个具有代表性的类来举例说明:

ClosureTask:

template <typename Closure>
class ClosureTask : public QueuedTask {
 public:
  explicit ClosureTask(Closure&& closure)
      : closure_(std::forward<Closure>(closure)) {}

 private:
  bool Run() override {
    closure_();
    return true;
  }

  typename std::decay<Closure>::type closure_;
};

lambda表达式,官方叫法叫 “闭包”,英文也就是 Closure.

这里ClosureTask为一个类模板, 它的成员就是一个 lambda表达式.

这里大家有没有想过为何要使用

typename std::decay::type closure_;

原因是对于lambda表达式,我们要同时支持 引用和移动构造的支持.

怎么来理解这句话呢?

我们在接的时候,要同时要支持以 拷贝构造和移动构造两种方式来接 lambda表达式的成员变量. (这句话大家理解下!)

我们来把它实例具体化:

移动构造:

功能:把lambda表达式,通过移动构造的方式接入进来.

auto func =[]{};
ClosrureTask task( std::move(func) );

这里面就必然涉及到类型的推导:(假设lambda表达式类型为Lambda类型,方便理解!)

explicit ClosureTask(Closure&& closure)

Closure类型: Lambda
closure 类型为: Lambda&&

typename std::decay::type closure_;等价于 Lambda closure_;

引用构造:

auto func =[]{};
ClosrureTask task( func );

explicit ClosureTask(Closure&& closure)

Closure类型: Lambda&
closure类型为: Lambda&& closure

正是因为Cosure类型为 Lambda& 类型,所以我们必须使用 std::decay 类做类型退化,目的是为了触发 lambda表达式成员变量的拷贝构造函数,否则就是直接引用 Lambda表达式的全套就会落入lambda表达式的陷阱。

typename std::decay::type closure_; 就等价于
typename std::decay< Lambda&>::type closure_;

展开后就是:Lambda closure_;

涉及知识点:

上面的分析,涉及到的知识点,主要是三个:

  1. 万能引用,也就 universal reference。
  2. 引用折叠
  3. 一些类的对象模型的概念

这三个要讲清楚,还需要花点功底和时间,同时需要结合大量测试例子进行结果追踪分析,这里篇幅肯定不够,我会再单独的开一篇文章来进行解释.

lambda表达式陷阱

lambda表达式最大的陷阱就是 捕捉的成员变量的生命周期, 比如:

{
	int a;
	int b;
	auto func = [&a,&b] {};
	// 把这个func添加到 task中去.
}

在{}后,a和b的生命周期就已经结束了,但是也许我们会把我们的lambda表达式给传递到我们的任务列表中,调用时候,忽然发现 a 和 b无法访问,导致程序出错,这一块 webrtc管理的还是相当严谨的,类似的使用场景一定要千万注意.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wh_shentu929

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值