【问题标题】:Why can't I call QMetaObject::invokeMethod(&threadObj, &QThread::start, Qt::QueuedConnection)?为什么我不能调用 QMetaObject::invokeMethod(&threadObj, &QThread::start, Qt::QueuedConnection)?
【发布时间】:2020-04-05 20:35:08
【问题描述】:

我有一个带有QCoreApplication 的控制台应用程序,在main() 中运行事件循环。 这里我有一个对象,它是 QThread 的派生对象,实现了 run() 以进行一些测试。 我希望线程在启动我的应用程序时启动,所以我调用了

QMetaObject::invokeMethod(&threadObj, &QThread::start, Qt::QueuedConnection);
return qapp.exec();

main()的末尾。

这是我的问题:为什么不能编译?

QThread::quit 执行相同操作可以正常工作,但QThread::exit 不行。 我认为这与startexit 的参数有关?`

编译器错误看起来像这样:

d:\dev\project\project\main.cpp(49): error C2665: 'QMetaObject::invokeMethod': none of the 4 overloads could convert all the argument types
1>x:\hmi_qt5\qt\msvc2017_x86\debug\include\qtcore\qobjectdefs.h(448): note: could be 'bool QMetaObject::invokeMethod(QObject *,const char *,QGenericArgument,QGenericArgument,QGenericArgument,QGenericArgument,QGenericArgument,QGenericArgument,QGenericArgument,QGenericArgument,QGenericArgument,QGenericArgument)' (compiling source file main.cpp)
1>x:\hmi_qt5\qt\msvc2017_x86\debug\include\qtcore\qobjectdefs.h(431): note: or       'bool QMetaObject::invokeMethod(QObject *,const char *,Qt::ConnectionType,QGenericArgument,QGenericArgument,QGenericArgument,QGenericArgument,QGenericArgument,QGenericArgument,QGenericArgument,QGenericArgument,QGenericArgument,QGenericArgument)' (compiling source file main.cpp)
1>x:\hmi_qt5\qt\msvc2017_x86\debug\include\qtcore\qobjectdefs.h(414): note: or       'bool QMetaObject::invokeMethod(QObject *,const char *,QGenericReturnArgument,QGenericArgument,QGenericArgument,QGenericArgument,QGenericArgument,QGenericArgument,QGenericArgument,QGenericArgument,QGenericArgument,QGenericArgument,QGenericArgument)' (compiling source file main.cpp)
1>d:\dev\project\project\main.cpp(49): note: while trying to match the argument list '(threadObj*, void (__thiscall QThread::* )(QThread::Priority), Qt::ConnectionType)'

提前致谢,

鲍勃

PS: 我确实有一个解决方法,可以通过使用仿函数或在threadObj 中实现自定义插槽。 我问这个是因为我很好奇为什么这不起作用,因为我认为语法很好。

【问题讨论】:

  • Qt 版本? C++ 标准版本?
  • @Superlokkus 我使用 Qt 5.12 和 C++11

标签: c++ qt qthread


【解决方案1】:

问题是存在QThread::start的参数,尽管是默认的,并且Qt在QMetaObject::invokeMethod最重要的重载被定义为const char *member作为第二个参数的方面被破坏了。因此,使用其中一种重载,您可以直接使用它:

#include <functional>

#include <QtWidgets/QApplication>
#include <QtWidgets/QtWidgets>
#include <QtWidgets/QMainWindow>

#include <QThread>

int main(int argc, char *argv[]) {
    QThread threadObj;
    QMetaObject::invokeMethod(&threadObj, "start", Qt::QueuedConnection);

    QApplication this_application(argc, argv);
    return this_application.exec();
}

另一种解决方案是

QMetaObject::invokeMethod(&threadObj, std::bind(&QThread::start,std::ref(threadObj),QThread::InheritPriority), Qt::QueuedConnection);

这与 lambda 几乎相同,但恕我直言,意图更清晰。

【讨论】:

    【解决方案2】:

    看来我找到了答案。 正如我最初预期的那样,问题不在于 Qt,而在于 C++。 使用函数指针进行间接函数调用时,只能调用具有空参数列表的函数。 (喜欢QThread::quit(void)) 当调用具有参数列表(包括默认参数)的函数时,它会隐式调用具有显式参数的函数。这在这里不起作用,因为函数指针中不能有参数。

    实现这一点的正确方法似乎是 lambda。

    QMetaObject::invokeMethod(&threadObj, [&threadObj]() {threadObj.start(); }, Qt::QueuedConnection);
    

    这样,你仍然可以在编译时检查语句的有效性。

    你好,鲍勃

    【讨论】:

    • 这不是C++的问题,而是invokeMethod函数缺少重载。从 C++11 开始,他们本可以完全放弃整个 Signal/Slot。
    • 丢弃信号和槽不会使对象之间的通信变得更加困难吗?
    • 并非如此,只是它不再隐藏 Qt 令人费解的混乱。我强烈建议任何初学者将 Qt 仅用于 GUI 逻辑。如果您有需要回调的长时间运行的函数,请使用 std::functionstd::future 或模板化参数,但不要在问题解决代码中包含一个 Qt 标头。即使使用完全不同的线程模型,我也做到了这一点,但优点是除了 GUI 之外不依赖 Qt:github.com/Superlokkus/s70357-it2-beleg 所以我开始在我的新项目中使用 Javascript 作为 GUI
    猜你喜欢
    • 1970-01-01
    • 2021-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多