Anschließen überlasteter Signale und Steckplätze in Qt 5

133

Ich habe Probleme, die neue Signal- / Slot-Syntax (unter Verwendung des Zeigers auf die Elementfunktion) in Qt 5 in den Griff zu bekommen, wie unter Neue Signal-Slot-Syntax beschrieben . Ich habe versucht, dies zu ändern:

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

dazu:

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

Beim Versuch, es zu kompilieren, wird jedoch eine Fehlermeldung angezeigt:

Fehler: Keine Übereinstimmungsfunktion für den Aufruf von QObject::connect(QSpinBox*&, <unresolved overloaded function type>, QSlider*&, void (QAbstractSlider::*)(int))

Ich habe es mit clang und gcc unter Linux versucht, beide mit -std=c++11.

Was mache ich falsch und wie kann ich das beheben?

dtruby
quelle
Wenn Ihre Syntax stimmt, könnte die einzige Erklärung sein, dass Sie nicht mit den Qt5-Bibliotheken verknüpfen, sondern stattdessen mit Qt4. Dies lässt sich mit QtCreator auf der Seite "Projekte" leicht überprüfen.
Matt Phillips
Ich habe einige Unterklassen von QObject (QSpinBox usw.) aufgenommen, so dass QObject hätte enthalten sein sollen. Ich habe auch versucht, dieses Include hinzuzufügen, und es wird immer noch nicht kompiliert.
Dtruby
Außerdem bin ich definitiv mit Qt 5 verbunden, ich verwende Qt Creator und die beiden Kits, mit denen ich beide teste, haben Qt 5.0.1 als Qt-Version.
Dtruby

Antworten:

243

Das Problem hierbei ist, dass es zwei Signale mit diesem Namen gibt: QSpinBox::valueChanged(int)und QSpinBox::valueChanged(QString). Ab Qt 5.7 stehen Hilfsfunktionen zur Auswahl der gewünschten Überlastung zur Verfügung, damit Sie schreiben können

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

Für Qt 5.6 und früher müssen Sie Qt mitteilen, welches Sie auswählen möchten, indem Sie es auf den richtigen Typ umwandeln:

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

Ich weiß, es ist hässlich . Aber daran führt kein Weg vorbei. Die heutige Lektion lautet: Überlasten Sie Ihre Signale und Slots nicht!


Nachtrag : Was an der Besetzung wirklich nervt, ist das

  1. man wiederholt den Klassennamen zweimal
  2. man muss den Rückgabewert angeben, auch wenn es normalerweise ist void(für Signale).

Daher habe ich manchmal dieses C ++ 11-Snippet verwendet:

template<typename... Args> struct SELECT { 
    template<typename C, typename R> 
    static constexpr auto OVERLOAD_OF( R (C::*pmf)(Args...) ) -> decltype(pmf) { 
        return pmf;
    } 
};

Verwendung:

connect(spinbox, SELECT<int>::OVERLOAD_OF(&QSpinBox::valueChanged), ...)

Ich persönlich finde es nicht wirklich nützlich. Ich erwarte, dass dieses Problem von selbst verschwindet, wenn Creator (oder Ihre IDE) automatisch die richtige Besetzung einfügt, wenn der Vorgang des Aufnehmens des PMF automatisch abgeschlossen wird. Aber in der Zwischenzeit ...

Hinweis: Die PMF-basierte Verbindungssyntax erfordert kein C ++ 11 !


Nachtrag 2 : In Qt 5.7 wurden Hilfsfunktionen hinzugefügt, um dies zu mildern, modelliert nach meiner obigen Problemumgehung. Der Haupthelfer ist qOverload(Sie haben auch qConstOverloadund qNonConstOverload).

Anwendungsbeispiel (aus den Dokumenten):

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)

Anhang 3 : Wenn Sie sich die Dokumentation eines überlasteten Signals ansehen, ist die Lösung des Überlastungsproblems jetzt in den Dokumenten selbst klar angegeben. Zum Beispiel sagt https://doc.qt.io/qt-5/qspinbox.html#valueChanged-1

Hinweis: Signal valueChanged ist in dieser Klasse überladen. Um mithilfe der Funktionszeigersyntax eine Verbindung zu diesem Signal herzustellen, bietet Qt einen praktischen Helfer zum Abrufen des Funktionszeigers, wie in diesem Beispiel gezeigt:

   connect(spinBox, QOverload<const QString &>::of(&QSpinBox::valueChanged),
[=](const QString &text){ /* ... */ });
peppe
quelle
1
Ah ja, das macht sehr viel Sinn. Ich denke für Fälle wie diesen, in denen die Signale / Slots überlastet sind, bleibe ich einfach bei der alten Syntax :-). Vielen Dank!
Dtruby
17
Ich war so aufgeregt über die neue Syntax ... jetzt ein kalter Spritzer eiskalter Enttäuschung.
RushPL
12
Für diejenigen, die sich fragen (wie ich): "pmf" steht für "Zeiger auf Mitgliedsfunktion".
Vicky Chijwani
14
Ich persönlich bevorzuge die static_castHässlichkeit gegenüber der alten Syntax, einfach weil die neue Syntax eine Überprüfung der Kompilierungszeit auf das Vorhandensein des Signals / Slots ermöglicht, in dem die alte Syntax zur Laufzeit fehlschlagen würde.
Vicky Chijwani
2
Leider ist es oft keine Option, ein Signal nicht zu überladen - Qt überlastet häufig die eigenen Signale. (zB QSerialPort)
PythonNut
14

Die Fehlermeldung lautet:

Fehler: Keine Übereinstimmungsfunktion für den Aufruf von QObject::connect(QSpinBox*&, <unresolved overloaded function type>, QSlider*&, void (QAbstractSlider::*)(int))

Der wichtige Teil davon ist die Erwähnung des " ungelösten überlasteten Funktionstyps ". Der Compiler weiß nicht, ob Sie meinen QSpinBox::valueChanged(int)oder QSpinBox::valueChanged(QString).

Es gibt eine Handvoll Möglichkeiten, um die Überlastung zu beheben:

  • Geben Sie einen geeigneten Vorlagenparameter an connect()

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

    Dies zwingt connect()dazu, sich &QSpinBox::valueChangedin die Überlastung aufzulösen , die eine benötigt int.

    Wenn Sie ungelöste Überladungen für das Slot-Argument haben, müssen Sie das zweite Template-Argument an angeben connect(). Leider gibt es keine Syntax, nach der der erste abgeleitet werden muss, sodass Sie beide angeben müssen. Dann kann der zweite Ansatz helfen:

  • Verwenden Sie eine temporäre Variable des richtigen Typs

    void(QSpinBox::*signal)(int) = &QSpinBox::valueChanged;
    QObject::connect(spinBox, signal,
                     slider,  &QSlider::setValue);

    Die Zuordnung zu signalwählt die gewünschte Überladung aus und kann nun erfolgreich in die Vorlage eingesetzt werden. Dies funktioniert genauso gut mit dem Argument 'slot', und ich finde es in diesem Fall weniger umständlich.

  • Verwenden Sie eine Konvertierung

    Wir können dies static_casthier vermeiden , da es sich lediglich um einen Zwang handelt, anstatt den Schutz der Sprache aufzuheben. Ich benutze so etwas wie:

    // Also useful for making the second and
    // third arguments of ?: operator agree.
    template<typename T, typename U> T&& coerce(U&& u) { return u; }

    Dies ermöglicht es uns zu schreiben

    QObject::connect(spinBox, coerce<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
                     slider, &QSlider::setValue);
Toby Speight
quelle
8

Eigentlich können Sie Ihren Slot einfach mit Lambda umwickeln und dies:

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

wird besser aussehen. : \

Newlifer
quelle
0

Die obigen Lösungen funktionieren, aber ich habe dies auf eine etwas andere Weise mithilfe eines Makros gelöst. Nur für den Fall, dass es hier ist:

#define CONNECTCAST(OBJECT,TYPE,FUNC) static_cast<void(OBJECT::*)(TYPE)>(&OBJECT::FUNC)

Fügen Sie dies in Ihren Code ein.

Dann Ihr Beispiel:

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

Wird:

QObject::connect(spinBox, CONNECTCAST(QSpinBox, double, valueChanged),
             slider, &QSlider::setValue);
Basile Perrenoud
quelle
2
Lösungen "über" was? Gehen Sie nicht davon aus, dass die Antworten allen in der Reihenfolge angezeigt werden, in der Sie sie gerade sehen!
Toby Speight
1
Wie verwenden Sie dies für Überladungen, die mehr als ein Argument benötigen? Verursacht das Komma keine Probleme? Ich denke, Sie müssen wirklich die Parens bestehen, dh #define CONNECTCAST(class,fun,args) static_cast<void(class::*)args>(&class::fun)- wie CONNECTCAST(QSpinBox, valueChanged, (double))in diesem Fall verwendet.
Toby Speight
Es ist ein schönes nützliches Makro, wenn Klammern für mehrere Argumente verwendet werden, wie in Tobys Kommentar
Ejectamenta