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

4

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)?
Dental answered 4/7, 2019 at 15:37 Comment(2)
But why? Doesn't v automatically reset when the lambda goes out of scope?Longcloth
@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.Dental
H
53
  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();
};
Hugohugon answered 4/7, 2019 at 15:39 Comment(0)
M
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.

Mizuki answered 4/7, 2019 at 15:39 Comment(0)
R
8

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

auto l = [v = std::move(u)] () mutable {
    v.reset();
};
Rothermere answered 4/7, 2019 at 15:39 Comment(0)
B
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(); 
    };
}
Birl answered 4/7, 2019 at 16:2 Comment(2)
It's likely that lambda is going to outlive it's declaring scope, so reference capture will dangleBench
@Bench It depends on the context.Birl

© 2022 - 2024 — McMap. All rights reserved.