When moving a unique_ptr into a lambda, why is it not possible to call reset?

3.7k views Asked by At

When moving std::unique_ptr into the lambda, it is not possible to call reset() on it, because it seems to be const then:

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. Why does this happen?
  2. Is it possible to capture the std::unique_ptr in another way which allows calling reset() within the lambda (with C++17 or later)?
4

There are 4 answers

0
leslie.yao On BEST ANSWER
  1. Why does this happen?

Because the function-call operator of a lambda,

Unless the keyword mutable was used in the lambda-expression, the function-call operator is const-qualified and the objects that were captured by copy are non-modifiable from inside this operator().

and

  1. Is it possible to capture the std::unique_ptr in another way which allows to call reset() within the lambda

You need to mark it mutable.

mutable: allows body to modify the parameters captured by copy, and to call their non-const member functions

e.g.

auto l = [v = std::move(u)]() mutable {
    v.reset();
};
0
eerorika On
  1. Why does this happen?

Because lambdas are by default non-mutable. Therefore all captured objects are const. reset is a non-const member function that modifies the unique pointer.

  1. Is it possible to capture the std::unique_ptr in another way which allows to call reset() within the lambda (with C++17 or later)?

Yes. Declare the lambda mutable:

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

This is possible since C++11 where lambdas were introduced. All captured non-const objects of a mutable lambda are non-const copies.

0
Jarod42 On

To mutate a "member" of the lambda, you need the mutable keyword:

auto l = [v = std::move(u)] () mutable {
    v.reset();
};
2
Vlad from Moscow On

Within the lambda its data members are immutable by default. You need to append the mutable specifier to the lambda expression.

As an alternative, you could capture the unique_ptr by reference, as for example:

#include <memory>

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