文字列ベースとファンクターベースの接続の違い
Qtには、C++でシグナルスロット接続を記述するための2つの異なる方法があります:文字列ベースの接続構文とファンクタベースの接続構文です。どちらの構文にも長所と短所があります。以下の表はその違いをまとめたものです。
文字列ベース | ファンクタベース | |
---|---|---|
型チェックは | ランタイム | コンパイル時 |
暗黙の型変換が可能 | Y | |
シグナルをラムダ式に接続できる | Y | |
シグナルより多くの引数を持つスロットにシグナルを接続できる(デフォルト・パラメータを使用) | Y | |
C++関数とQML関数を接続することができる | Y |
以下のセクションでは、これらの違いについて詳しく説明し、それぞれの接続構文に特有な機能の使い方を示します。
型チェックと暗黙の型変換
文字列ベースの接続は、実行時に文字列を比較することで型チェックを行います。この方法には3つの制限があります:
- 接続エラーはプログラムの実行開始後にしか検出できない。
- シグナルとスロットの間で暗黙の変換を行うことができない。
- 型定義と名前空間を解決できない。
制限2と3が存在するのは、文字列コンパレータがC++型情報にアクセスできず、正確な文字列マッチングに依存しているためです。
対照的に、ファンクタ・ベースの接続はコンパイラによってチェックされます。コンパイラはコンパイル時にエラーを検出し、互換性のある型間の暗黙の変換を可能にし、同じ型の異なる名前を認識します。
例えば、int
を伝送するシグナルを、double
を受け付けるスロットに接続するには、ファンクタベースの構文しか使用できません。QSlider はint
の値を保持し、QDoubleSpinBox はdouble
の値を保持します。次のスニペットは、これらを同期させる方法を示しています:
auto slider = new QSlider(this); auto doubleSpinBox = new QDoubleSpinBox(this); // OK: The compiler can convert an int into a double connect(slider, &QSlider::valueChanged, doubleSpinBox, &QDoubleSpinBox::setValue); // ERROR: The string table doesn't contain conversion information connect(slider, SIGNAL(valueChanged(int)), doubleSpinBox, SLOT(setValue(double)));
次の例は、名前解決の欠如を示している。QAudioInput::stateChanged()は、"QAudio::State "型の引数で宣言されています。したがって、文字列ベースの接続は、"State"
がすでに表示されていても、"QAudio::State" も指定しなければなりません。引数の型は接続の一部ではないため、この問題はファンクタベースの接続には当てはまりません。
auto audioInput = new QAudioInput(QAudioFormat(), this); auto widget = new QWidget(this); // OK connect(audioInput, SIGNAL(stateChanged(QAudio::State)), widget, SLOT(show())); // ERROR: The strings "State" and "QAudio::State" don't match using namespace QAudio; connect(audioInput, SIGNAL(stateChanged(State)), widget, SLOT(show())); // ...
ラムダ式への接続
ファンクタベースの接続構文では、シグナルを C++11 ラムダ式に接続できます。この機能は、文字列ベースの構文では使用できません。
次の例では、TextSender クラスがQString パラメータを持つtextCompleted()
シグナルを発信しています。以下はクラス宣言です:
class TextSender : public QWidget { Q_OBJECT QLineEdit *lineEdit; QPushButton *button; signals: void textCompleted(const QString& text) const; public: TextSender(QWidget *parent = nullptr); };
ユーザがボタンをクリックすると、TextSender::textCompleted()
:
TextSender::TextSender(QWidget *parent) : QWidget(parent) { lineEdit = new QLineEdit(this); button = new QPushButton("Send", this); connect(button, &QPushButton::clicked, [=] { emit textCompleted(lineEdit->text()); }); // ... }
この例では、QPushButton::clicked()とTextSender::textCompleted()
に互換性のないパラメータがあるにもかかわらず、ラムダ関数によって接続がシンプルになっている。対照的に、文字列ベースの実装では、余分なボイラープレート・コードが必要になります。
注: ファンクタベースの接続構文は、スタンドアロン関数や通常のメンバ関数を含むすべての関数へのポインタを受け付けます。しかし、可読性を高めるために、シグナルはスロット、ラムダ式、その他のシグナルにのみ接続されるべきです。
C++ オブジェクトと QML オブジェクトの接続
文字列ベースの構文では C++ オブジェクトと QML オブジェクトを接続することができますが、 ファンクタベースの構文では接続することができません。なぜなら、QML の型は実行時に解決されるため、C++ コンパイラでは利用できないからです。
次の例では、QMLオブジェクトをクリックするとC++オブジェクトがメッセージを表示し、その逆も同様です。以下に QML の型(QmlGui.qml
)を示します:
Rectangle { width: 100; height: 100 signal qmlSignal(string sentMsg) function qmlSlot(receivedMsg) { console.log("QML received: " + receivedMsg) } MouseArea { anchors.fill: parent onClicked: qmlSignal("Hello from QML!") } }
これがC++のクラスです:
クラスCppGui :publicQWidget{Q_OBJECT QPushButton*シグナル:voidcppSignal(constQVariant&sentMsg)const;public slots:voidcppSlot(constQString&receivedMsg)const{; public slots: void cppSlot(const&receivedMsg)const qDebug() << "C++ received:" << receivedMsg; }public: CppGui(QWidget*parent =nullptr) : QWidget(parent) { button= newQPushButton("Click Me!", this); connect(button, &::clicked, [=] { cppシグナルを出す。QPushButton::clicked, [=]{ cppSignal("Hello from C++!"); }); } };
シグナル・スロットの接続を行うコードを以下に示す:
auto cppObj = new CppGui(this); auto quickWidget = new QQuickWidget(QUrl("QmlGui.qml"), this); auto qmlObj = quickWidget->rootObject(); // Connect QML signal to C++ slot connect(qmlObj, SIGNAL(qmlSignal(QString)), cppObj, SLOT(cppSlot(QString))); // Connect C++ signal to QML slot connect(cppObj, SIGNAL(cppSignal(QVariant)), qmlObj, SLOT(qmlSlot(QVariant)));
QVariant 注: QMLのJavaScript関数は、型アノテーションを使用しない限り、すべてvar
型のパラメータをとります。詳細はQMLメソッドの起動を参照してください。
QPushButton がクリックされると、コンソールは'QML received:「と表示されます。同様に、矩形をクリックすると、コンソールに'C++ received:「と表示されます。
C++ オブジェクトと QML オブジェクトを対話させる他の方法については、C++ から QML オブジェクトを対話させるを参照してください。
スロットのデフォルトパラメータを使って少ないパラメータでシグナルに接続する
通常、スロットの引数がシグナルと同じ数(あるいはそれ以下)であり、すべての引数の型に互換性がある場合にのみ、接続を行うことができます。
文字列ベースの接続構文では、このルールを回避することができます。スロットにデフォルトのパラメータがある場合、それらのパラメータをシグナルから省略することができます。スロットより少ない引数でシグナルが発行された場合、Qtはデフォルトのパラメータ値を使用してスロットを実行します。
ファンクタベースの接続はこの機能をサポートしていません。
デフォルト引数を持つスロットprintNumber()
を持つDemoWidget
というクラスがあるとします:
public slots:voidprintNumber(intnumber= 42) { デフォルト引数を持つ次のようなスロットがあるとする。 qDebug() << "Lucky number" << number; }
文字列ベースの接続を使用すると、DemoWidget::printNumber()
は、後者が引数を持っていなくても、QApplication::aboutToQuit ()に接続できる。ファンクター・ベースの接続では、コンパイル時にエラーが発生する:
DemoWidget::DemoWidget(QWidget*(parent) : QWidget(parent) {// OK: printNumber() は、デフォルト値42で呼び出されます。 connect(qApp, SIGNAL(aboutToQuit()), this,SLOT(printNumber()));// ERROR:コンパイラーは互換性のある引数を要求します connect(qApp, &QCoreApplication::aboutToQuit, this, &DemoWidget::printNumber); }
ファンクタベースの構文でこの制限を回避するには、シグナルをスロットを呼び出すラムダ関数に接続します。上記の「ラムダ式への接続」を参照してください。
オーバーロードされたシグナルとスロットの選択
文字列ベースの構文では、パラメータの型を明示的に指定します。その結果、オーバーロードされたシグナルやスロットのインスタンスが明確になります。
対照的に、ファンクターベースの構文では、オーバーロードされたシグナルやスロットは、どのインスタンスを使用するかをコンパイラに伝えるためにキャストしなければなりません。
例えば、QLCDNumber には、display()
スロットの3つのバージョンがある:
QLCDNumber::display(int)
QLCDNumber::display(double)
QLCDNumber::display(QString)
int
バージョンをQSlider::valueChanged ()に接続するには、2つの構文があります:
auto slider = new QSlider(this); auto lcd = new QLCDNumber(this); // String-based syntax connect(slider, SIGNAL(valueChanged(int)), lcd, SLOT(display(int))); // Functor-based syntax connect(slider, &QSlider::valueChanged, lcd, qOverload<int>(&QLCDNumber::display));
qOverload()も参照のこと 。
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.