αλεχολυτ
αλεχολυτ

Reputation: 5039

Make lambda non-copyable/non-movable

Consider the following code:

#include <iostream>
#include <thread>

int main() {
    std::thread t;
    const auto l = [x = std::move(t)]{};
    decltype(l) m = std::move(l);
}

This code doesn't compile with the following messages:

prog.cc: In function 'int main()':
prog.cc:7:32: error: use of deleted function 'main()::<lambda()>::<lambda>(const main()::<lambda()>&)'
    7 |     decltype(l) m = std::move(l);
      |                                ^
prog.cc:6:37: note: 'main()::<lambda()>::<lambda>(const main()::<lambda()>&)' is implicitly deleted because the default definition would be ill-formed:
    6 |     const auto l = [x = std::move(t)]{};
      |                                     ^
prog.cc:6:37: error: use of deleted function 'std::thread::thread(const std::thread&)'
In file included from prog.cc:2:
/opt/wandbox/gcc-head/include/c++/10.0.1/thread:154:5: note: declared here
  154 |     thread(const thread&) = delete;
      |     ^~~~~~

Is there a way to make lambda non-copyable or non-movable without explicit capturing an any non-copyable variable (i.e. leaving [] empty)?

Upvotes: 3

Views: 306

Answers (1)

You can write a simple enveloping aggregate that will prevent the move and copy.

struct NoCopyMove {
    NoCopyMove(NoCopyMove const&) = delete;
    NoCopyMove(NoCopyMove&&)      = delete;

    void operator=(NoCopyMove const&) = delete;
    void operator=(NoCopyMove&&)      = delete;
};

template<class Functor>
struct Fixed : Functor, NoCopyMove {
    using Functor::operator();
};

template<typename F>
Fixed (F&&) -> Fixed<std::decay_t<F>>;

To be used like this

const auto l = Fixed{[]{}};

NoCopyMove is a simple mixin that disables copying and moving. Writing it this way allows us to keep the specializations of Fixed as simple aggregates.

All Fixed does is inherit/initialize as base (with guaranteed copy elision when possible) the functor it's being given as an argument. And then expose its operator().

Since there are no members involved (other then maybe in Functor), and since a lambda cannot possibly inherit from our custom NoCopyMove class, the empty base optimization kicks in for stateless lambdas. So objects are not expected to be any larger than the lambda you initialize them with.

Upvotes: 7

Related Questions