前言
用过qt信号与槽机制的人应该都知道,qt中connect函数可以有两种写参数的方式,第一种是SIGNAL()和SLOT()宏,第二种是&App::member 函数指针,昨天突然发现有时候用第二种方式编译会报错
no matching member function for call to 'connect'
用第一种宏函数的时候就不会报错,并且能编译通过,因此对这两种方式做了一下区分
一、先说结论
第一种用SIGNAL和SLOT宏的方式,信号函数参数列表数量小于槽函数参数列表数量的情况下 可以通过编译,但是会运行时报错
qt.core.qobject.connect: QObject::connect: Incompatible sender/receiver arguments
同时,使用宏函数的方式可以显式指定信号与槽函数的重载版本
并且此方式无需考虑类的继承关系,编译器智能识别基类的槽函数
第二种用&App::member 函数指针的方式,信号函数参数列表数量小于槽函数参数列表数量的情况下 不可以通过编译,同时会显示报错
no matching member function for call to 'connect'
使用函数指针的方式,无法指定信号与槽函数的重载版本,如果函数有重载,则会产生二义性冲突
二、错误原因及原理
1.SIGNAL和SLOT宏的工作原理
宏函数将信号和槽函数转换为const char *类型的字符串,并调用connect的如下重载版本
QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal,
const QObject *receiver, const char *method,
Qt::ConnectionType type = Qt::AutoConnection)
将信号与槽函数转换为字符串之后,编译器不会考虑参数是否匹配,只能编译后运行时进行异常检测,并且由于是显示写出了参数列表,指定了函数的重载版本,因此不存在二义性冲突
2.&App::member 函数指针的工作原理
&App::member函数指针形式将自身作为参数传递给connect的如下重载版本
template <typename PointerToMemberFunction>
QMetaObject::Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal,
const QObject *receiver, PointerToMemberFunction method,
Qt::ConnectionType type = Qt::AutoConnection)
可以看出信号与槽函数使用同一个模板参数<typename PointerToMemberFunction>
,因此当信号与槽函数参数一致时调用此重载版本
template <typename Func1, typename Func2>
static inline typename
std::enable_if<QtPrivate::FunctionPointer<Func2>::ArgumentCount == -1, QMetaObject::Connection>::type
connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
const QObject *context, Func2 slot,
Qt::ConnectionType type = Qt::AutoConnection)
可以看出当信号与槽函数参数不一致时,调用此重载版本,但是由于函数指针调用时无法显式指定信号与槽函数的重载版本,即会产生二义性冲突
例如:
QPushbutton *button;
QSignalMapper *signal_mapper;
connect(button, &QPushButton::clicked, signal_mapper, &QSignalMapper::map);
上例中QSignalMapper类具有map的两个不同重载版本
void QSignalMapper::map(QWidget*);
void QSignalMapper::map();
因此会报错
no matching member function for call to 'connect'
qt6 官方文档对于QSignalMapper的Detailed Description中的案例,就是这样写的,但是实际编译时会报错
总结
1、在信号与槽函数不存在重载版本的时候,才可以使用函数指针的方式进行connect连接,否则编译会有二义性冲突
2、任何情况都可以使用SIGNAL与SLOT宏函数进行连接,都可以编译成功,但是可能会运行时异常报错