但是C++11却没有加入移动捕获特性,但是标准委员会在C++14提出了一种新的捕获机制,叫做 初始化捕获(init capture)。
使用初始化捕获,你将得到机会指定:
- 由lambda生成的闭包类的成员变量的名字
- 一个表达式,用以初始化该成员变量
以下是如何使用初始化捕获将 std::unique_ptr
移动到闭包内的:
class Widget {
public:
...
bool isValidated() const;
bool isProcessed() const;
bool isArchived() const;
private:
...
};
auto pw = std::make_unique<Widget>();
...
auto func = [pw = std::move(pw)]
{ return pw->isValidated()
&& pw->isArchived(); };
位于 =
左侧的,是闭包类成员变量的名字,而位于右侧是是其初始化表达hi。上述捕获列表中将 pw
移入闭包内,实现了移动捕获。
可以看出,C++11“捕获”的概念在C++14中得到了泛化,因为在C++11中无法捕获一个表达式的结果。因此,初始化捕获还有另一个名字:广义lambda捕获 。
如果你一定要在C++11中用lambda表达式按移动捕获,只需要:
- 把需要捕获的对象移动到
std::bind
产生的函数对象中。 - 给到lambda表达式一个指向要捕获的对象的引用。
这是C++14的广义捕获:
std::vector<double> data;
...
auto func = [data = std::move(data)]
{...};
在C++11中可以这样模拟:
std::vector<double> data;
...
auto func =
std::bind(
[](const std::vector<double>& data)
{...},
std::move(data);
);
const
修饰形参是为了保证lambda的常量语义,为了防止绑定对象里移动构造得到的 data
副本被意外修改,lambda的形参就声明为常量引用。
但是如果lambda表达式被 mutable
修饰,lambda的形参就不需要 const
修饰:
auto func =
std::bind(
[](std::vector<double>& data) mutable
{...},
std::move(data);
);
绑定对象是由源对象复制来的,绑定对象的生命期和闭包相同,所以针对绑定对象和闭包里的对象可以采用相同的手法加之处置。