文字列ベースとファンクターベースの接続の違い

Qtには、C++でシグナルスロット接続を記述するための2つの異なる方法があります:文字列ベースの接続構文とファンクタベースの接続構文です。どちらの構文にも長所と短所があります。以下の表はその違いをまとめたものです。

文字列ベースファンクタベース
型チェックはランタイムコンパイル時
暗黙の型変換が可能Y
シグナルをラムダ式に接続できるY
シグナルより多くの引数を持つスロットにシグナルを接続できる(デフォルト・パラメータを使用)Y
C++関数とQML関数を接続することができるY

以下のセクションでは、これらの違いについて詳しく説明し、それぞれの接続構文に特有な機能の使い方を示します。

型チェックと暗黙の型変換

文字列ベースの接続は、実行時に文字列を比較することで型チェックを行います。この方法には3つの制限があります:

  1. 接続エラーはプログラムの実行開始後にしか検出できない。
  2. シグナルとスロットの間で暗黙の変換を行うことができない。
  3. 型定義と名前空間を解決できない。

制限2と3が存在するのは、文字列コンパレータがC++型情報にアクセスできず、正確な文字列マッチングに依存しているためです。

対照的に、ファンクタ・ベースの接続はコンパイラによってチェックされます。コンパイラはコンパイル時にエラーを検出し、互換性のある型間の暗黙の変換を可能にし、同じ型の異なる名前を認識します。

例えば、int を伝送するシグナルを、double を受け付けるスロットに接続するには、ファンクタベースの構文しか使用できません。QSliderint の値を保持し、QDoubleSpinBoxdouble の値を保持します。次のスニペットは、これらを同期させる方法を示しています:

    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つのバージョンがある:

  1. QLCDNumber::display(int)
  2. QLCDNumber::display(double)
  3. 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.