2

I'm new to modern C++ and I think I'm missing some key points about lambda functions and smart pointers that are giving me some trouble when trying to sandbox in the boost libraries.

platform: RHEL7, c++17

I want to pass ownership of a unique_ptr first to a lambda function and then to a function nested inside of the lambda. Here are a few test cases that I need some help.

class TestClass{
    private:
        std::string message;
    public:
        TestClass(std::string message): message(message){}
        void printMessage(){std::cout << message << std::endl;}
};

void functionNestedInLambda(std::unique_ptr<TestClass> testClass){
    testClass->printMessage();
    return;
}

int main(){
    auto testClass = std::make_unique<TestClass>("Hello Stack Overflow");
    auto lFunction = [&](){functionNestedInLambda(std::move(testClass));};
    lFunction();
    return 0;
}

This compiles fine, and seems to pass ownership of testClass to functionNestedInLambda(). The "pass by reference" within the lambda's capture clause is giving me pause, as I'm not sure how the ownership of testClass is getting passed to the lambda function. My thought then was try to explicitly transfer ownership to the lambda in the same way as seen in this stack overflow post: How to capture a unique_ptr into a lambda expression?.

I changed

auto lFunction = [&](){functionNestedInLambda(std::move(testClass));};

to

auto lFunction = [testClass = std::move(testClass)](){functionNestedInLambda(std::move(testClass));};

This creates the following compiler error

error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = TestClass; _Dp = std::default_delete<TestClass>]’
  139 |     auto lFunction = [testClass = std::move(testClass)](){functionNestedInLambda(std::move(testClass));};

It seems to me that this should pass the ownership first to the lambda and then to the nested function, but I'm obviously missing something.

What would be the best way to accomplish this? I want to control object ownership as tightly as possible.

Also, how exactly is the first example compiling? How is ownership handled when passing in the unique pointer as reference to the lambda?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
GuitarCava
  • 21
  • 2
  • 3
    Add `mutable` to your lambda. Otherwise, this `std::move(testClass)` triggers a copy operation. – rafix07 Jul 09 '21 at 15:30
  • @rafix07 Thanks! that seemed to have fixed the problem. Is it the `std::move(testClass)` in the capture clause that causes the copy operation? – GuitarCava Jul 09 '21 at 15:38
  • By default `operator()` of closure is marked as `const`, so you can only read data members like `testClass`, with `mutable` data member can be modfied - can be moved (source object is modified) by `std::move(testClass)` otherwise, copy is made which fails for `unique_ptr` as non-copyable. – rafix07 Jul 09 '21 at 15:39
  • @rafix07 Thanks for the info! If you post the answer, I'll accept it as best answer. – GuitarCava Jul 09 '21 at 15:45
  • 1
    @GuitarCava no, the copy operation is the `move()` in the `functionNestedInLambda()` function call, since the captured `testClass` will be `const` (and thus unmovable) without the lambda being marked `mutable` – Remy Lebeau Jul 09 '21 at 15:46

0 Answers0