简体   繁体   中英

Can't add a non-copyable connection to boost::signals2::signal

I was implementing a wrapper that would allow me to perfectly forward my handler (in this case Widget object) into a callable that I can then use as a signal handler.

I managed to do this by forwarding my handler as a tuple and therefore when the lvalue ref is passed as an argument to add function, the tuple stores reference to my object. And in this case, everything seems to be working fine.

However, it fails when I'm trying to pass an object of a non-copyable class by value. The reason is that the copy constructor is implicitly deleted - and of course, that's true but I can't really grasp why is it needed to have copy ctor available? Is there any rationale behind this or am I perhaps having a bug in my code that I haven't noticed?

Compiler Explorer link

Compiled with clang 14, C++20 enabled

#include <boost/signals2/signal.hpp>
#include <tuple>
#include <boost/signals2/signal.hpp>
#include <iostream>

struct Widget
{
  Widget() = default;
  Widget(const Widget&) = delete;
  Widget& operator=(const Widget&) = delete;
  Widget(Widget&&) noexcept = default;
  Widget& operator=(Widget&&) noexcept = default;
  ~Widget() noexcept = default;

  void operator()()
  {
    std::cout << "Test\n";
  }
};

struct SigWrapper
{
  boost::signals2::signal<void()> sig;

  template<class Handler>
  void add(Handler&& handler)
  {
    //I need to wrap it here
    auto wrapper = [handler_as_tuple = std::forward_as_tuple(std::forward<Handler>(handler))]() mutable {
      //[...]
      std::invoke(std::get<0>(handler_as_tuple));
    };

    sig.connect(std::move(wrapper));
  }

  void call()
  {
    sig();
  }
};

int main()
{
  auto w = SigWrapper{};
  auto widget = Widget{};
  w.add(widget); //ok: add as lvalue reference, will be wrapped into tuple that will hold ref to Widget
  w.add(Widget{}); //error:
  //copy constructor of '' is implicitly deleted because field '' has a deleted copy constructor

  w.call();
}

/opt/compiler-explorer/libs/boost_1_79_0/boost/signals2/detail/slot_template.hpp:160:26: error: call to implicitly-deleted copy constructor of '(lambda at

:29:20)' _slot_function = detail::get_invocable_slot(f, detail::tag_type(f)); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /opt/compiler-explorer/libs/boost_1_79_0/boost/signals2/detail/slot_template.hpp:85:9: note: in instantiation of function template specialization 'boost::signals2::slot::init_slot_function:29:20)>' requested here init_slot_function(f); ^:34:17: note: in instantiation of function template specialization 'boost::signals2::slot::slot:29:20)>' requested here sig.connect(std::move(wrapper)); ^:48:5: note: in instantiation of function template specialization 'SigWrapper::add' requested here w.add(Widget{}); //error: ^:29:21: note: copy constructor of '' is implicitly deleted because field '' has a deleted copy constructor auto wrapper = [handler_as_tuple = std::forward_as_tuple(std::forward(handler))]() mutable { ^ /opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/tuple:744:17: note: explicitly defaulted function was implicitly deleted here constexpr tuple(const tuple&) = default; ^ /opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/tuple:599:19: note: copy constructor of 'tuple' is implicitly deleted because base class '_Tuple_impl' has a deleted copy constructor class tuple: public _Tuple_impl ^ /opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/tuple:435:17: note: explicitly defaulted function was implicitly deleted here constexpr _Tuple_impl(const _Tuple_impl&) = default; ^ /opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/tuple:408:7: note: copy constructor of '_Tuple_impl' is implicitly deleted because base class '_Head_base' has a deleted copy constructor: private _Head_base ^ /opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/tuple:185:17: note: explicitly defaulted function was implicitly deleted here constexpr _Head_base(const _Head_base&) = default; ^ /opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/tuple:224:13: note: copy constructor of '_Head_base' is implicitly deleted because field '_M_head_impl' is of rvalue reference type 'Widget &&' _Head _M_head_impl; ^ /opt/compiler-explorer/libs/boost_1_79_0/boost/function/function_template.hpp:1145:21: note: passing argument to parameter 'f' here operator=(Functor f) ^

Signals2 is thread-safe. I imagine the copy-constructible requirement is a consequence of this. Eg this guarantee seems hard to implement without copying the slot functions:

Note, even after a connection is disconnected, its's associated slot may still be in the process of executing. In other words, disconnection does not block waiting for the connection's associated slot to complete execution. This situation may occur in a multi-threaded environment if the disconnection occurs concurrently with signal invocation, or in a single-threaded environment if a slot disconnects itself.

The usual ways to transform a shared object to a copyable one apply (eg using std::shared_ptr ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM