在同一命令中使用变量时移动到 lambda

Move to lambda while using the variable in the same command

本文关键字:变量 移动 lambda 一命 命令      更新时间:2023-10-16

让我们考虑以下旨在毁灭世界的结构。

#include <iostream>
#include <vector>
struct SkyNet {
    std::vector<int> terminators;
    SkyNet() = default;
    SkyNet(SkyNet&& that)
    {
        printf("Move constructn");
        this->terminators = std::move(that.terminators);
    }
};
class Apocalypse {
public:
    template <typename Lambda>
    void run(Lambda lambda) {
        lambda();
    }
};
Apocalypse create_apocalypse(const SkyNet& net) {
    std::cout << "Create: " << net.terminators.size() << " terminatorsn";
    return Apocalypse();
}

(请原谅这些结构的不良设计和其他缺陷。创建它只是为了演示特定问题。

然后我们有简单的主:

int main()
{
    SkyNet net;
    net.terminators.push_back(10);
    create_apocalypse(net)
    .run([net = std::move(net)] {
        std::cout << "Lambda: " << net.terminators.size() << " terminatorsn";
    });
    return 0;
}

其中输出:

Move construct
Create: 0 terminators
Lambda: 1 terminators

这意味着run函数的参数将在调用链接其他事件的函数之前创建(评估?create_apocalypse函数。正确吗?这是先计算表达式的所有参数(不仅是第一个函数的参数)然后调用函数的一般规则吗?

创建 lambda 时,您正在创建一个匿名对象。定义闭包时,将定义其构造函数和成员。

所以这相当于:

crete_apocalypse(net).run(MyLambda{std::move(net)});

这在语义上等同于类似的东西

run(create_apocalypse(net), MyLambda{std::move(net)});

由于函数参数的计算顺序未由标准指定,因此实现可以自由选择。换句话说,它可以选择先做create_apocalypse(net),然后按照你似乎期望的那样做,或者它可以反过来(在你的例子中是这样做的)并首先构建MyLambda,从而移动net,以便一旦它到达create_apocalypse(),它就已经移动了。

当它真正重要时,你不应该依赖于你为特定编译器获得的行为。避免它并保持正确性的方法是这样写:

auto a = create_apocalypse(net);
a.run([net = std::move(net)] { /* ... */ });

演示