新しいシグナルスロット構文 で説明されているように、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)
)にtwoシグナルがあるということです。 Qt 5.7から、目的のオーバーロードを選択するためのヘルパー関数が提供されています。
connect(spinbox, qOverload<int>(&QSpinBox::valueChanged),
slider, &QSlider::setValue);
Qt 5.6以前では、適切なタイプにキャストすることにより、Qtに選択するものを指示する必要があります。
connect(spinbox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
slider, &QSlider::setValue);
私は知っている、それはugいです。しかし、これを回避する方法はありません。今日のレッスンは:信号とスロットを過負荷にしないでください!
補遺:キャストについて本当に迷惑なのは
void
(シグナル用)であっても、戻り値を指定する必要があります。だから私は時々この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), ...)
個人的にはあまり役に立たないと思います。 PMFを取得する操作をオートコンプリートするときに、Creator(またはIDE)が自動的に正しいキャストを挿入すると、この問題は自然に解消されると思います。しかし、その間に...
注: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)
補遺3:過負荷信号のドキュメントを見ると、過負荷問題の解決策がドキュメント自体に明確に記載されています。たとえば、 https://doc.qt.io/qt-5/qspinbox.html#valueChanged-1 は
注:このクラスでは、シグナルvalueChangedがオーバーロードされています。関数ポインター構文を使用してこの信号に接続するために、Qtは次の例に示すように関数ポインターを取得するための便利なヘルパーを提供します。
connect(spinBox, QOverload<const QString &>::of(&QSpinBox::valueChanged), [=](const QString &text){ /* ... */ });
エラーメッセージは次のとおりです。
エラー:
QObject::connect(QSpinBox*&, <unresolved overloaded function type>, QSlider*&, void (QAbstractSlider::*)(int))
の呼び出しに一致する関数がありません
これの重要な部分は、「未解決のオーバーロードされた関数タイプ」の言及です。コンパイラは、QSpinBox::valueChanged(int)
とQSpinBox::valueChanged(QString)
のどちらを意味するのかを知りません。
オーバーロードを解決する方法はいくつかあります。
connect()
に提供しますQObject::connect<void(QSpinBox::*)(int)>(spinBox, &QSpinBox::valueChanged,
slider, &QSlider::setValue);
これにより、connect()
が&QSpinBox::valueChanged
をint
を取るオーバーロードに強制的に解決します。
スロット引数に未解決のオーバーロードがある場合は、connect()
に2番目のテンプレート引数を指定する必要があります。残念ながら、最初に推論するように要求する構文はないため、両方を指定する必要があります。そのとき、2番目のアプローチが役立ちます。
void(QSpinBox::*signal)(int) = &QSpinBox::valueChanged;
QObject::connect(spinBox, signal,
slider, &QSlider::setValue);
signal
への割り当てにより、目的のオーバーロードが選択され、テンプレートに正常に置換できるようになります。これは「スロット」引数でも同じように機能し、その場合は面倒ではありません。
ここでは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)>(&QSpinBox::valueChanged),
slider, &QSlider::setValue);
実際には、ラムダとこれでスロットをラップすることができます:
connect(spinbox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
slider, &QSlider::setValue);
良くなります。 :\
上記の解決策は機能しますが、マクロを使用してこれをわずかに異なる方法で解決しました。
#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);