为什么在 lambda 中移动时不会调用移动构造函数?

19
我正在尝试编译以下代码:
#include <utility>

struct C2 {
        C2() = default;
        C2(C2 const&) = delete;
        C2(C2&&) = default;
};

int main() {
        C2 p2;
        ([p2_1{ std::move(p2) }]() {
                auto p2_2 = std::move(p2_1); // <---
        })();
        return 0;
}

然而,这段代码无法编译并出现错误,指出对 p2_2 的赋值调用了被删除的函数,即拷贝构造函数。请注意,对 p2_1 的移动是可以的。为什么没有使用移动构造函数?


我不是lambda专家,但是为什么不直接通过引用捕获p2并将其直接移动到p2_2,而不是将p2移动到p2_1,捕获p2_1,然后将p2_1移动到p2_2呢? [&p2]() { auto p2_2 = std::move(p2); } - Remy Lebeau
2
@RemyLebeau 这只是我实际操作的大大简化版本。我一直在删除部分,直到得到一个仍然存在问题的小样本。实际代码需要 lambda 生存于声明它的函数作用域之外,因此不能使用引用捕获。 - Baruch
好的,那就有道理了。 - Remy Lebeau
1个回答

27

这里的关键是,lambda所代表的匿名类类型的operator()默认为const。这意味着你不能从p2_1移动,因为该函数中lambda的thisconst&。你需要做的是使用mutable关键字,例如:

int main() {
        C2 p2;
        ([p2_1{ std::move(p2) }]() mutable {
                auto p2_2 = std::move(p2_1); // <---
        })();
        return 0;
}

这使得该函数不是const,这意味着您可以更改其成员。这可以让您移动p2_1而不是尝试复制它。


这也适用于捕获this吗?我似乎能够使用已捕获的this指针调用非const函数。 - Baruch
@baruch 这是一个好问题。据我所知,当你捕获this时,它会保留其值类型,因此如果它是非const的,则在lambda中它也将是非const的。 - NathanOliver
3
@baruch 的意思是,this 是一个常量(const),但 *this 不是。它是一个常量指针(const pointer),而不是指向常量的指针(pointer-to-const)。 - user253751
1
@baruch:请注意,NathanOliver答案中的“this”与捕获“this”的结果非常不同!当NathanOliver说“由于其thisconst&”,他指的是指向闭包对象本身的指针。但是,如果您在lambda主体内部实际键入关键字“this”,则不会获取指向闭包对象的指针;您将获取(并按值捕获)指向*包含lambda表达式的成员函数的对象的指针。在C++中,除非使用类似Y组合器之类的东西,否则无法命名Nathan所说的实体。 - Quuxplusone

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接