如何在不存储连接的情况下断开lambda函数

Nic*_*aus 12 c++ lambda qt c++11

有没有办法断开Qt对lambda函数的连接而不存储连接对象?

我知道如果我存储QMetaObject::Connection从connect函数返回的内容是可能的,但我真的不想这样做,因为会有很多它们.我主要连接到lambda函数,以避免创建一堆一次性方法和对象,似乎我需要做所有SLOTs更优选的簿记.

Dav*_*ing 20

假设连接:

QObject::connect(senderInstance, &Sender::mySignal, this, []() {
    // implement slot as a lambda
});
Run Code Online (Sandbox Code Playgroud)

然后您可以轻松断开连接:

QObject::disconnect(senderInstance, &Sender::mySignal, this, nullptr);
Run Code Online (Sandbox Code Playgroud)

这将断开所有this's插槽Sender::mySignal; 但是,只有一个这样的插槽是很常见的,因此最终的结果是简单地执行断开并且没有副作用.


mab*_*abg 8

您可以使用虚拟对象:

QObject *obj = new QObject(this);
QObject::connect(m_sock, &QLocalSocket::readyRead, obj, [this](){
   obj->deleteLater();
Run Code Online (Sandbox Code Playgroud)

当obj被销毁时,连接断开,因为你在连接上传递了obj.


Yak*_*ont 6

这是隐藏簿记问题的两种方法。

首先,我们维护一个std::vector销毁该销毁资源的源:

typedef std::shared_ptr<void> listen_token;

struct disconnecter {
  QMetaObject::Connection conn;
  disconnecter(   QMetaObject::Connection&& c ):conn(std::move(c)) {}
  ~disconnecter() { QObject::disconnect(conn); }
};

template<class F, class T, class M>
listen_token QtConnect( T* source, M* method, F&& f ) {
  return std::make_shared<disconnecter>(
    QObject::connect( source, method, std::forward<F>(f));
  );
}

typedef std::vector<listen_token> connections;
Run Code Online (Sandbox Code Playgroud)

然后我们如下连接:

connections conns;
conns.emplace_back( QtConnect( bob, &Bob::mySignal, [](QString str){ std::cout << "Hello World!\n"; } ) );
Run Code Online (Sandbox Code Playgroud)

当向量被破坏时,连接对象也被破坏。

这类似于处理其他信号/插槽系统的方式,侦听器跟踪令牌,然后将其返回。但是在这里,我将断开连接对象保留为不透明类型,以清除破坏时的连接。

请注意,复制该向量将延长连接的寿命。如果消息将发送到类的特定实例,connections则将一个实例存储在该类中,实例销毁后您将不会收到消息。


基于@lpapp找到的第二种方法是,如果您有一个lambda想要只响应一次信号而调用一次,然后断开连接:

template<class F>
struct auto_disconnect_t {
  F f;
  std::shared_ptr<QMetaObject::Connection> conn;

  template<class U>
  auto_disconnect_t(U&& u):
    f(std::forward<U>(u)),
    conn(std::make_shared<QMetaObject::Connection>())
  {}

  template<class... Args>
  void operator()(Args&&... args)const{
    QObject::disconnect(*conn);
    f( std::forward<Args>(args)... );
  }
};

template<class T, class M, class F>
void one_shot_connect( T* t, M* m, F&& f ) {
  typedef typename std::decay<F>::type X;
  auto_disconnect_t<X> helper(std::forward<F>(f));
  *helper.conn = QObject::connect( t, m, helper );
};
Run Code Online (Sandbox Code Playgroud)

我们在这里one_shot_connect( bob, &Bob::mySignal, [](QString str) { std::cout << "Hello\n" } );,下次信号触发时,我们会收到消息,然后断开连接。

我会在处理您的Lambda之前断开连接,以防万一Lambda导致信号触发或其他原因。

  • 为什么在这里使用shared_ptr而不是unique_ptr? (2认同)
  • @povman `std::function` 和概念 *invoke* 的大多数类型擦除也需要 *copy*(在上面的第二种情况下),而 `unique` 会阻止 *copy*。在第一种情况下,`unique` 可以工作,但需要更多的输入(存储显式销毁器)或更少的抽象(在令牌中存储显式目标,而不是 `void`)。而其他监听器-观察器系统需要监听器令牌中的数据不同,所以我习惯于使用`shared_ptr&lt;void&gt;`。 (2认同)