Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make lambda non-copyable/non-movable

Tags:

c++

lambda

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)?

like image 541
αλεχολυτ Avatar asked Jul 26 '26 11:07

αλεχολυτ


1 Answers

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.

like image 79
StoryTeller - Unslander Monica Avatar answered Jul 28 '26 01:07

StoryTeller - Unslander Monica



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!