连接Qt 5中的过载信号和插槽
我在使用Qt 5中的新信号/插槽语法(使用指向成员函数的指针)时遇到了困难,如新信号插槽语法中所述 。 我试着改变这个:
QObject::connect(spinBox, SIGNAL(valueChanged(int)), slider, SLOT(setValue(int)); 对此:
 QObject::connect(spinBox, &QSpinBox::valueChanged, slider, &QSlider::setValue); 
但是当我尝试编译它时遇到错误:
错误:没有匹配函数调用
QObject::connect(QSpinBox*&, <unresolved overloaded function type>, QSlider*&, void (QAbstractSlider::*)(int))
 我已经在Linux上用clang和gcc尝试过了,都用-std=c++11 。 
我做错了什么,我该如何解决?
 这里的问题是有两个信号的名称: QSpinBox::valueChanged(int)和QSpinBox::valueChanged(QString) 。 您需要通过将其转换为正确的types来告诉Qt您要select哪一个: 
 connect(spinbox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), slider, &QSlider::setValue); 
我知道,这很丑 。 但是这是没有办法的。 今天的教训是: 不要超载你的信号和插槽!
附录 :演员真正讨厌的是这个
- 一个重复类名两次
-  即使通常void(对于信号),也必须指定返回值。
所以我发现自己很less使用这个C ++ 11代码片段:
 template<typename... Args> struct SELECT { template<typename C, typename R> static constexpr auto OVERLOAD_OF( R (C::*pmf)(Args...) ) -> decltype(pmf) { return pmf; } }; 
用法:
 connect(spinbox, SELECT<int>::OVERLOAD_OF(&QSpinBox::valueChanged), ...) 
我个人觉得这不是很有用。 当创build者(或您的IDE)自动完成取出PMF的操作时,我希望这个问题会自动消失。 但在此期间…
注意:基于PMF的连接语法不需要C ++ 11 !
  附录2 :在Qt 5.7中,辅助函数被添加来缓解这个问题,仿照我上面的解决方法。 主要的助手是qOverload (你也得到了qConstOverload和qNonConstOverload )。 
用法示例(来自文档):
 struct Foo { void overloadedFunction(); void overloadedFunction(int, QString); }; // requires C++14 qOverload<>(&Foo:overloadedFunction) qOverload<int, QString>(&Foo:overloadedFunction) // same, with C++11 QOverload<>::of(&Foo:overloadedFunction) QOverload<int, QString>::of(&Foo:overloadedFunction) 
错误消息是:
错误:没有匹配函数调用
QObject::connect(QSpinBox*&, <unresolved overloaded function type>, QSlider*&, void (QAbstractSlider::*)(int))
 这个重要的部分是提到“ 无法parsing的重载函数types ”。 编译器不知道你的意思是QSpinBox::valueChanged(int)还是QSpinBox::valueChanged(QString) 。 
解决过载的方法有很多:
 提供一个合适的模板参数connect() 
 QObject::connect<void(QSpinBox::*)(int)>(spinBox, &QSpinBox::valueChanged, slider, &QSlider::setValue); 
 这迫使connect()将&QSpinBox::valueChangedparsing为需要int的重载。 
 如果对于slot参数有无法parsing的重载,则需要为connect()提供第二个模板参数。 不幸的是,没有语法要求首先推断,所以你需要提供。 这是第二种方法可以帮助的时候: 
使用正确types的临时variables
 void(QSpinBox::*signal)(int) = &QSpinBox::valueChanged; QObject::connect(spinBox, signal, slider, &QSlider::setValue); 
  signal分配将select所需的过载,现在可以将其成功replace为模板。 这与“插槽”的说法同样适用,在这种情况下,我发现它不那么麻烦。 
使用转换
 我们可以在这里避免使用static_cast ,因为它只是强制而不是去除语言的保护。 我使用类似于: 
 // Also useful for making the second and // third arguments of ?: operator agree. template<typename T, typename U> T&& coerce(U&& u) { return u; } 
所以我们现在可以写
 QObject::connect(spinBox, coerce<void(QSpinBox::*)(int)>(signal), slider, &QSlider::setValue); 
其实,你可以用lambda包装你的插槽,这个:
 connect(spinbox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), slider, &QSlider::setValue); 
会看起来更好。 :\
上面的解决scheme工作,但我用一个稍微不同的方式,使用macros来解决这个问题,所以在这里是这样的:
 #define CONNECTCAST(OBJECT,TYPE,FUNC) static_cast<void(OBJECT::*)(TYPE)>(&OBJECT::FUNC) 
在你的代码中添加这个。
那么,你的例子:
 QObject::connect(spinBox, &QSpinBox::valueChanged, slider, &QSlider::setValue); 
变为:
 QObject::connect(spinBox, CONNECTCAST(QSpinBox, double, valueChanged), slider, &QSlider::setValue);