Rotartsi
Rotartsi

Reputation: 567

C++ `std::move` custom type into a lambda capture

I have a class in which I deleted the copy assignment operator and copy constructor, leaving only a move assignment operator and move constructor. For example:

struct Number {
  public:
  int *pNum;

  Number &operator=(const Number &rhs) = delete;
  Number(const Number &rhs) = delete;

  Number &operator=(Number &&rhs) {
    if (&rhs == this) {
      return *this;
    }

    pNum = rhs.pNum;
    rhs.pNum = nullptr;
    return *this;
  }

  Number() = default;

  Number(Number &&rhs) {
    *this = std::move(rhs);
  }

  ~Number() {
    delete pNum;
  }
};

Now, I want to use std::move to capture this class into a lambda. For example:

int main() {
  std::function<int(int)> add;


  int a = 3;

  {

    Number n{};
    n.pNum = new int;
    *n.pNum = 5;

    add = [&, capA{std::move(n)}](int) mutable -> int {
      int b = *capA.pNum; // 5
      return a + b; // 8
    };
  }

  std::cout << add(3);
}

However, it seems that n would be const so that c++ would try to use the deleted copy constructor. How would I fix this? (REPL: https://repl.it/@25GrantY/WeirdLambda)

Upvotes: 1

Views: 971

Answers (2)

Alexey Polyudov
Alexey Polyudov

Reputation: 164

the problem here is coming from std::function. This class must satisfy CopyConstructible and CopyAssignable requirements, according to documentation. https://en.cppreference.com/w/cpp/utility/functional/function

std::function satisfies the requirements of CopyConstructible and CopyAssignable.

When you attempt to initialize std::function with a lambda that captured move-only type by value, such assignment (if successful) would violate the both of the requirements above.

This is why compilation fails with an error.

How to fix this?

Either do not use std::function, or make your lambda copyable.

You can use std::shared_ptr as a copyable wrapper for move-only type.

auto shared_number = std::make_shared<Number>{};

Now, you can pass shared_number to lambda, and assign to std::function.

Upvotes: 5

bolov
bolov

Reputation: 75727

The problem isn't with the lambda. E.g. this works:

auto l = [&, capA{std::move(n)}](int) mutable -> int {
      int b = *capA.pNum; // 5
      return a + b; // 8
    };

The problem is with std::function and this lambda. Because you capture a non-copyable object then the lambda is non-copyable. std::function requires its stored object to be copyable so that's why it doesn't work.

You can read more here: Move-only version of std::function

Upvotes: 7

Related Questions