【问题标题】:Passing std::unique_ptr to a member function, then passing it to a lambda function inside a QT thread connection将 std::unique_ptr 传递给成员函数,然后将其传递给 QT 线程连接内的 lambda 函数
【发布时间】:2017-08-08 14:11:44
【问题描述】:

我一直在尝试做一些类似的事情(提供以下内容是为了演示编译错误,所以如果你想编译它你可以,仅此而已)

//main.cpp
#include <QApplication>
#include "test.h"

int main(int argc, char* argv[])
{
    QApplication a(argc, argv);
    Test t;
    std::string s = "hi ";
    std::unique_ptr<std::string> sptr = std::make_unique<std::string>(s);
    t.foo(sptr);
    return a.exec();
}

测试.h

//test.h
#ifndef TEST_H
#define TEST_H
#include <QObject>
#include <QThread>
#include <string>
#include <QApplication>
#include <memory>

class Test : public QObject{
    Q_OBJECT
public:
    void foo(std::unique_ptr<std::string> t_ptr);
    void bar(std::unique_ptr<std::string> t_ptr);
};


#endif //TEST_H

test.cpp

//test.cpp
#include "test.h"
#include <iostream>

void Test::foo(std::unique_ptr<std::string> t_ptr){
    QThread * thread = new QThread(this);
    moveToThread(thread);
    auto lambda = [=, uptr = std::move(t_ptr) ]()mutable{bar(uptr);};
    connect(thread, &QThread::started, lambda);
    connect(thread, &QThread::finished, thread, &QThread::deleteLater);
    thread->start();
}
void Test::bar(std::unique_ptr<std::string> t_ptr){
    t_ptr->append("hello");
    std::cout << *t_ptr << std::endl;
    moveToThread(QApplication::instance()->thread());
}

但无论我做什么,我似乎都无法编译,我最终得到的只是这个:

 error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = std::__cxx11::basic_string<char>; _Dp = std::default_delete<std::__cxx11::basic_string<char> >]'
     auto lambda = [=, uptr = std::move(t_ptr) ]()mutable{bar(uptr);};
                                                                  ^
    note: declared here
       unique_ptr(const unique_ptr&) = delete;

或者除了那个错误之外,还有这个错误:

In member function 'void Test::foo(std::unique_ptr<T>)':
error: use of deleted function 'Test::foo(std::unique_ptr<std::__cxx11::basic_string<char> >)::<lambda()>::<lambda>(const Test::foo(std::unique_ptr<std::__cxx11::basic_string<char> >)::<lambda()>&)'
 connect(thread, &QThread::started, lambda);

更改为mutable 也会导致相同的错误,显然我已经使用了here 找到的答案。

我确定这与 lambda 捕获使unique_ptr 成为常​​量这一事实有关,但是我不确定如何才能真正完成我想要实现的目标,而实际上却使unique_ptr 成为成员多变的。

编辑:

好的,我在其他人评论之前尝试了这些东西,但它们也没有奏效,但因为我收到了关于“试试这个”的回复,所以我将向您展示其他一些不起作用的排列:

使用 std::move(uptr)(同样的错误)

connect中创建Lambda

connect(thread, &QThread::started, [=, uptr = std::move(sensor_ptr) ]()mutable{bar(uptr);};);

同样的错误。

bar改成右值,同样的错误

更改bar 为 const 值,同样的错误

bar 改为 const rvalue,同样的错误

【问题讨论】:

  • bar(std::move(uptr)); 在 lambda 中。如果您仍然看到错误,请发布minimal reproducible example。您可能还需要 std::move(lambda); 这里 - connect(thread, &amp;QThread::started, lambda);,但我不确定 connect 是如何工作的。
  • @Praetorian qt 的东西不相关吗?为何如此?这也是非常小的......
  • @Praetorian,我也使用了 std::move 并没有解决任何问题,它仍然认为我正在使用已删除的 const unique_ptr 构造函数...
  • 示例不完整,其他人无法编译。 Qt 的东西可能是相关的,但第一步是把它拿出来,看看你是否还能重现这个问题。如果不能,则一次将其添加回一件事。
  • @Praetorian Ok 更新了可验证的示例,同样的错误。

标签: c++ multithreading qt lambda c++14


【解决方案1】:

错误信息表明无法复制 lambda

可以理解,因为您的 lambda 是一个以 std::unique_ptr&lt;T&gt; 为成员的类 - 并且无法默认复制此类。

QObject::connect 接受函子,并且可能将其存储在内部某个地方。而且无法复制您的 lambda。

考虑例子:

template<typename T>
void UseFunctor(T i_functor)
{
    T functor_backup = i_functor; // fails to compile because of copying lambda
    // ...
    functor_backup();
}

int main() 
{
    std::unique_ptr<int> p_int = std::make_unique<int>(10);
    UseFunctor([p_ptr = std::move(p_int)](){});
}

【讨论】:

    【解决方案2】:

    您需要将uptr 移动到bar,并且可能将lambda 移动到connect。我也不知道1连接的目的地是否需要CopyConstructible/CopyAssignable,如果他们这样做了,你无能为力。

    1 - QT 文档对这些事情保持沉默。

    【讨论】:

    • 最初我连接了整个 lambda,它做了同样的事情,不幸的是我无法显示我尝试过的每一个排列。
    • 我认为您只需将 unique_ptr 替换为 shared_ptr 即可将其与 QThread::started 一起使用。或者,您可以使用std::thread,它在其构造函数中完美转发
    • 不幸的是,我不相信我可以使用 std::thread 而不进行重大更改,因为我需要将我的 QObject 移到那里(qt 有一些奇怪的线程语法)
    【解决方案3】:

    std::unique_ptr&lt;&gt;不能复制,只能转发,否则持有的指针不唯一。

    如果你想将指针的所有权转移到一个函数,你只能声明unique_ptr参数如下:

    void func(std::unique_ptr<foo>& bar);    // as a non-const reference
    void func2(std::unique_ptr<foo>&& bar);  // using the move operator
    void func3(std::unique_ptr<boo> bar);    // by value, call as func2(std::move(bar));
    
    // call as
    
    func(unique);
    func2(unique);  // or the more expressive func2(std::move(unique)); 
    func3(std::move(unique));
    

    如果您不想转让所有权,最好将包含的对象作为裸指针或引用传递。

    void func(foo* bar);  // could be const pointer
    
    // call as 
    func(unique.get());
    
    // or...
    
    void func(foo& bar);  // could be const reference
    
    // call as 
    func(*unique.get());
    

    将 unique_ptr 传递给 lambda 捕获也是如此。

    【讨论】:

      猜你喜欢
      • 2015-09-03
      • 2013-01-16
      • 2012-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-27
      • 2018-03-12
      相关资源
      最近更新 更多