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

Move to lambda while using the variable in the same command

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

#include <iostream>
#include <vector>

struct SkyNet {
    std::vector<int> terminators;

    SkyNet() = default;
    SkyNet(SkyNet&& that)
    {
        printf("Move construct\n");
        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() << " terminators\n";
    return Apocalypse();
}

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

然后我们有简单的 main:

int main()
{
    SkyNet net;
    net.terminators.push_back(10);

    create_apocalypse(net)
    .run([net = std::move(net)] {
        std::cout << "Lambda: " << net.terminators.size() << " terminators\n";
    });
    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)] { /* ... */ });

Demo