35

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)?
curiousguy
  • 8,038
  • 2
  • 40
  • 58
Roi Danton
  • 7,933
  • 6
  • 68
  • 80
  • But why? Doesn't `v` automatically reset when the lambda goes out of scope? – L. F. Jul 06 '19 at 00:25
  • @L.F. Yes it does and its destructor works with a `const` ptr. However, in case the lifetime of the pointer needs to end before the end of the scope (or another pointer should be assigned or the ownership of the managed object should be transferred to another unique_ptr), you need a non-const unique_ptr. – Roi Danton Jul 06 '19 at 21:58

4 Answers4

52
  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();
};
Justin
  • 24,288
  • 12
  • 92
  • 142
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
14
  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.

eerorika
  • 232,697
  • 12
  • 197
  • 326
8

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

auto l = [v = std::move(u)] () mutable {
    v.reset();
};
Justin
  • 24,288
  • 12
  • 92
  • 142
Jarod42
  • 203,559
  • 14
  • 181
  • 302
4

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(); 
    };
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335