При перемещении unique_ptr в лямбду, почему нельзя вызвать сброс?

При перемещении std::unique_ptr в лямбду невозможно вызвать метод reset() для него, потому что тогда он выглядит как const:

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 другим способом, который позволяет вызывать reset() внутри лямбды (с С++ 17 или новее)?

Ответ 1

  1. Почему это происходит?

Потому что оператор вызова функции лямбды,

Если ключевое слово mutable не использовалось в лямбда-выражении, оператор вызова функции является константно квалифицированным, и объекты, которые были захвачены копией, не модифицируются внутри этого operator().

а также

  1. Можно ли std::unique_ptr другим способом, который позволяет вызывать reset() внутри лямбды?

Вы должны пометить его как mutable.

mutable: позволяет body изменять параметры, захваченные копией, и вызывать их неконстантные функции-члены

например

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

Ответ 2

  1. Почему это происходит?

Потому что лямбды по умолчанию не изменяемы. Поэтому все захваченные объекты являются постоянными. reset является неконстантной функцией-членом, которая изменяет уникальный указатель.

  1. Можно ли перехватить std :: unique_ptr другим способом, который позволяет вызывать reset() внутри лямбды (с С++ 17 или новее)?

Да. Объявите лямбда-переменную:

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

Это возможно начиная с С++ 11, где были введены лямбды. Все захваченные неконстантные объекты изменяемой лямбды являются неконстантными копиями.

Ответ 3

Чтобы изменить "член" лямбды, вам нужно ключевое слово mutable:

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

Ответ 4

Внутри лямбды его данные по умолчанию неизменны. Вам нужно добавить mutable спецификатор к лямбда-выражению.

В качестве альтернативы вы можете захватить unique_ptr по ссылке, например:

#include <memory>

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