当将unique_ptr移动到lambda中时,为什么无法调用reset?

35

std::unique_ptr 移入 lambda 后,无法调用 reset(),因为它似乎是常量:

error C2662: void std::unique_ptr<int,std::default_delete<_Ty>>::reset(int *) noexcept': cannot convert 'this' pointer from 'const std::unique_ptr<int,std::default_delete<_Ty>>' to 'std::unique_ptr<int,std::default_delete<_Ty>> &

#include <memory>
int main()
{
    auto u = std::unique_ptr<int>();
    auto l = [v = std::move(u)]{
        v.reset(); // this doesn't compile
    };
}
  1. 为什么会发生这种情况?
  2. 是否有其他方式可以捕获std::unique_ptr,使得在lambda函数内部可以调用reset()(使用C++17或更高版本)?

但是为什么?当 lambda 超出范围时,v 不会自动重置吗? - L. F.
@L.F. 是的,它可以,并且它的析构函数可以使用const指针。然而,如果指针的生命周期需要在作用域结束之前结束(或者需要分配另一个指针或将托管对象的所有权转移给另一个unique_ptr),则需要一个非const unique_ptr。 - Roi Danton
4个回答

52
  1. 为什么会发生这种情况?

因为一个lambda的函数调用运算符,

除非在lambda表达式中使用了关键字mutable,否则函数调用运算符是const限定的,并且通过复制捕获的对象在该operator()内部是不可修改的。

以及

  1. 有没有可能以另一种方式捕获std::unique_ptr,从而允许在lambda内部调用reset()

你需要将其标记为mutable

mutable:允许函数体修改通过复制捕获的参数,并调用它们的非const成员函数

例如:

auto l = [v = std::move(u)]() mutable {
    v.reset();
};

14
  1. 为什么会发生这种情况?

因为Lambda默认是不可变的,因此所有被捕获的对象都是const类型。而reset()是一个非const成员函数,可以修改unique_ptr的值。

  1. 是否可能以另一种方式捕获std::unique_ptr,使其允许在lambda中调用reset()(使用C++17或更高版本)?

是的。将lambda声明为mutable即可:

[captures](arguments) mutable { body }
                      ^^^^^^^

自从C++11引入lambda表达式以来,这是可能的。所有被可变lambda捕获的非const对象都是非const副本。


8

要改变 lambda 中的“成员”,你需要使用关键字 mutable

auto l = [v = std::move(u)] () mutable {
    v.reset();
};

4

默认情况下,lambda表达式内的数据成员是不可变的。您需要在lambda表达式中添加 mutable 说明符。

作为替代方案,您可以通过引用捕获 unique_ptr ,例如:

#include <memory>

int main()
{
    auto u = std::unique_ptr<int>();
    auto l = [&v = u]{
        v.reset(); 
    };
}

3
Lambda表达式可能会在其声明作用域外继续存在,因此引用捕获将会悬空。 - Caleth
1
@Caleth 这取决于上下文。 - Vlad from Moscow

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