При перемещении unique_ptr в лямбду, почему нельзя вызвать сброс? - PullRequest
30 голосов
/ 04 июля 2019

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

Ответы [ 4 ]

50 голосов
/ 04 июля 2019
  1. Почему это происходит?

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

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

и

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

Вам нужно пометить mutable.

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

например,

auto l = [v = std::move(u)]() mutable {
    v.reset();
};
14 голосов
/ 04 июля 2019
  1. Почему это происходит?

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

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

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

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

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

8 голосов
/ 04 июля 2019

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

auto l = [v = std::move(u)] () mutable {
    v.reset();
};
5 голосов
/ 04 июля 2019

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

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

#include <memory>

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