Qt 5でオーバーロードされた信号とスロットを接続する


133

新しいシグナルスロット構文で説明されているように、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

何が悪いのですか、どうすれば修正できますか?


構文が正しい場合は、Qt5ライブラリにリンクしていないことが唯一の説明である可能性がありますが、代わりにQt4などです。これは、「プロジェクト」ページのQtCreatorで簡単に確認できます。
マットフィリップス

QObjectのいくつかのサブクラス(QSpinBoxなど)を含めたので、QObjectを含めるべきでした。私はそのインクルードも追加しようとしましたが、それでもコンパイルされません。
dtruby 2013年

また、私は間違いなくQt 5にリンクしています。QtCreatorを使用しています。両方でテストしている2つのキットには、QtバージョンとしてQt 5.0.1が記載されています。
dtruby 2013年

回答:


244

ここでの問題があるということです2:その名前の信号をQSpinBox::valueChanged(int)QSpinBox::valueChanged(QString)。Qt 5.7以降、目的のオーバーロードを選択するためのヘルパー関数が提供されているため、次のように記述できます。

connect(spinbox, qOverload<int>(&QSpinBox::valueChanged),
        slider, &QSlider::setValue);

Qt 5.6以前の場合、正しいタイプにキャストすることで、選択するQtをQtに指示する必要があります。

connect(spinbox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
        slider, &QSlider::setValue);

私は知っています、それは醜いです。しかし、これを回避する方法はありません。今日のレッスンは次のとおりです。信号とスロットに過負荷をかけないでください!


補遺:キャストについて本当に迷惑なのは、

  1. クラス名を2回繰り返す
  2. 通常の場合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){ /* ... */ });

1
ええ、それは非常に理にかなっています。シグナル/スロットがオーバーロードしているこのようなケースでは、古い構文をそのまま使用します:-)。ありがとう!
dtruby 2013年

17
私は新しい構文にとても興奮していました...今、凍えるような失望の冷たいスプラッシュ。
RushPL 2013年

12
(私と同じように)不思議に思う人のために:「pmf」は「メンバー関数へのポインター」を表します。
Vicky Chijwani

14
私は個人的にstatic_cast、古い構文よりも醜さを好みます。単に新しい構文により、実行時に古い構文が失敗する信号/スロットの存在をコンパイル時にチェックできるからです。
Vicky Chijwani 2015

2
残念ながら、信号をオーバーロードしないことはしばしばオプションではありません。Qtはしばしば自分の信号をオーバーロードします。(例QSerialPort
PythonNut 2016年

14

エラーメッセージは次のとおりです。

エラー:の呼び出しに一致する関数がありません 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

    スロット引数の未解決のオーバーロードがある場合は、2番目のテンプレート引数をに指定する必要がありますconnect()。残念ながら、最初の推論を求める構文はないため、両方を指定する必要があります。そのとき、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);

8

実際には、ラムダとこれでスロットをラップすることができます:

connect(spinbox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
    slider, &QSlider::setValue);

良く見えるでしょう。:\


0

上記の解決策は機能しますが、マクロを使用してこれを少し異なる方法で解決しました。

#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);

2
何が「上」のソリューション?回答が現在表示されている順序で全員に提示されるとは限りません。
Toby Speight 2017

1
複数の引数を取るオーバーロードにこれをどのように使用しますか?コンマは問題を引き起こしませんか?私はあなたが本当に括弧を渡す必要があると思います、すなわち#define CONNECTCAST(class,fun,args) static_cast<void(class::*)args>(&class::fun)- CONNECTCAST(QSpinBox, valueChanged, (double))この場合のように使用されます。
Toby Speight 2017

それはトビーさんのコメントのように、ブラケットは複数の引数に使用されている素敵な便利なマクロです
ejectamenta
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.