【问题标题】:Why is the copy constructor invoked in a lambda while moving a `unique_ptr`? [duplicate]为什么在移动 `unique_ptr` 时在 lambda 中调用复制构造函数? [复制]
【发布时间】:2021-02-18 15:14:19
【问题描述】:

为什么这段代码编译失败?

#include <memory>
#include <utility>

int foo() {
    auto num = std::make_unique<int>(1);
    auto func = [s = std::move(num)] {
        auto u = std::move(s); <-- ERROR!
        return *u;
    };
    return func();
}

错误是:

<source>:8:14: error: call to deleted constructor of 'std::unique_ptr<int, std::default_delete<int>>'
        auto u = std::move(s);
             ^   ~~~~~~~~~~~~
/opt/compiler-explorer/gcc-10.2.0/lib/gcc/x86_64-linux-gnu/10.2.0/../../../../include/c++/10.2.0/bits/unique_ptr.h:468:7: note: 'unique_ptr' has been explicitly marked deleted here
      unique_ptr(const unique_ptr&) = delete;

我不知道为什么要调用复制构造函数,这显然被设计为unique_ptr 删除了。我可以理解什么错误是什么,但不知道为什么它首先存在。

这就是我认为会发生的情况,但我不确定。如果我将这个 lambda 解压成一种 struct 并带有 operator() 作为

template <typename T>
struct Lambda{
  :
  :
  operator() const{
    auto u = std::move(s); // <-- error
  }
  private:
  std::unique_ptr<T> s;
};

我认为这将无法编译,因为 move(s) 会更改 s 的值,而这在 const 函数中是不允许的。因此,引用 lamda 的不变性,编译应该失败。甚至可以通过将 lambda 更改为 mutable 来修复此错误。但另一个似乎是让s 成为shared_ptr(根据我的说法,这应该失败,因为 lambda 仍然是不可变的),这就是我感到困惑的地方。

我在clanggcc 上都试过这个,结果相似。那么,请有人帮忙消除我的理解上的差距吗?

【问题讨论】:

  • Lambda 捕获,如 s,默认为 const。尝试在{ 之前添加mutable。 (你的 lambda 也缺少参数列表?)
  • No argument list works as parameter list is optional as per the 4th avatar here and I've try to add mutable to the lambda that did fix it,正如我的问题中已经提到的.我更倾向于破译编译器错误。还是谢谢!
  • 好的,如果下面的答案不充分,请告诉我们,我们会进一步澄清。
  • @rustyx 我已经接受了答案。使用shared_ptr 的替代方法有其自身的行为。我已将我的 cmets 更新为以下答案。如果您有其他内容,请添加。

标签: c++ lambda unique-ptr


【解决方案1】:

这是因为您试图在 const 成员函数中移动数据成员。

std::move(s) 的类型和类别为const unique_ptr&amp;&amp;。没有过载unique_ptr(const unique_ptr&amp;&amp;),唯一可行的是unique_ptr(const unique_ptr&amp;)

我认为这将无法编译,因为 move(s) 会更改 s 的值,而这在 const 函数中是不允许的。

不,move 只是 static_cast

【讨论】:

  • 我非常了解您回答的第一部分。关于move,我的意思是类似于this。如您所见,num 在 lambda 构造过程中被更改并变为 null。同样s 也应该变成null,不是吗,类似于num 根据构造函数#10 here
  • 我认为这可能是与原始问题不同的问题,并且与 this 有关,所以在我的情况下,s 实际上并没有被修改,因此代码编译时没有 @987654340 @关键字。
  • 设置s为null的是移动构造函数,而不是move调用
  • 好的。所以只是为了澄清我确实理解你在说什么,std::move 只是一个简单的转换,它为该对象提供了一个右值引用。实际的无效化是在使用这个右值引用创建的对象的移动构造函数中完成的,它只是移动传入对象的内存。
  • 是的,没错
猜你喜欢
  • 1970-01-01
  • 2014-01-24
  • 2018-02-03
  • 2015-06-10
  • 1970-01-01
相关资源
最近更新 更多