1. ホーム
  2. c++

[解決済み] Qt 5 でオーバーロードされた信号とスロットを接続する

2022-04-27 07:10:49

質問

Qt 5 の新しいシグナル/スロット構文(メンバー関数へのポインターを使用)を理解するのに苦労しています。 シグナルスロットの新しい構文 . これを変更してみた。

QObject::connect(spinBox, SIGNAL(valueChanged(int)),
                 slider, SLOT(setValue(int));

をこれに変更します。

QObject::connect(spinBox, &QSpinBox::valueChanged,
                 slider, &QSlider::setValue);

が、コンパイルしようとするとエラーになります。

error: no matching function for call to QObject::connect(QSpinBox*&, <unresolved overloaded function type>, QSlider*&, void (QAbstractSlider::*)(int))

Linuxでclangとgccで試しましたが、どちらも -std=c++11 .

何が間違っているのか、どうすれば直るのか?

解決方法は?

ここで問題となるのは という名前のシグナルがあります。 QSpinBox::valueChanged(int)QSpinBox::valueChanged(QString) . 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);

そうなんです、それは 醜い . でも、これは仕方がないことなんです。今日のレッスンは シグナルやスロットに負荷をかけないようにしましょう


追記 : キャストで本当に困るのは

  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), ...)

個人的にはあまり意味がないと思っています。Creator(またはIDE)がPMFを取得する操作をオートコンプリートする際に、自動的に正しいキャストを挿入するようになれば、この問題は自然に解消されると思います。しかし、その一方で...

注意:PMFベースの接続構文 はC++11を必要としません !


補遺2 Qt 5.7 では、この問題を解決するためのヘルパー関数が追加され、上記の私の回避策に倣っています。主なヘルパーは qOverload (他にも qConstOverload qNonConstOverload ).

使用例(docsより)。

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){ /* ... */ });