it-swarm.com.de

Überlastete Signale und Steckplätze in Qt verbinden 5

Ich habe Probleme mit der neuen Signal/Slot-Syntax (mit Zeiger auf Member-Funktion) in Qt 5, wie in Neue Signal Slot-Syntax beschrieben. Ich habe versucht, das zu ändern:

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

zu diesem:

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

ich erhalte jedoch eine Fehlermeldung, wenn ich versuche, es zu kompilieren:

fehler: keine passende Funktion 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?

104
dtruby

Das Problem hier 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 Überladung zur Verfügung, damit Sie schreiben können

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

Für Qt 5.6 und frühere Versionen müssen Sie Qt mitteilen, welchen Sie auswählen möchten, indem Sie den richtigen Typ auswählen:

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

Ich weiß, es ist hässlich. Aber daran gibt es keinen Weg. Die heutige Lektion lautet: Überlasten Sie Ihre Signale und Slots nicht!


Addendum: was an der Besetzung wirklich nervt, ist das 

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

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;
    } 
};

Verwendungszweck:

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

Ich persönlich finde es nicht wirklich nützlich. Ich gehe davon aus, dass dieses Problem von selbst behoben wird, wenn Creator (oder Ihre IDE) automatisch den richtigen Cast einfügt, wenn der PMF-Vorgang automatisch abgeschlossen wird. Aber in der Zwischenzeit ...

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


Addendum 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 qConstOverload und 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)
202
peppe

Die Fehlermeldung lautet:

fehler: keine passende Funktion für Aufruf an QObject::connect(QSpinBox*&, <unresolved overloaded function type>, QSlider*&, void (QAbstractSlider::*)(int))

Der wichtige Teil davon ist die Erwähnung von " nicht aufgelöster überlasteter Funktionstyp ". Der Compiler weiß nicht, ob Sie QSpinBox::valueChanged(int) oder QSpinBox::valueChanged(QString) meinen.

Es gibt einige Möglichkeiten, die Überlastung zu beheben:

  • Bereitstellen eines geeigneten Vorlagenparameters für connect()

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

    Dies zwingt connect(), &QSpinBox::valueChanged in die Überladung aufzulösen, die eine int benötigt.

    Wenn Sie nicht aufgelöste Überladungen für das Slot-Argument haben, müssen Sie das zweite Vorlagenargument an connect() übergeben. Leider gibt es keine Syntax, um nach der ersten zu fragen, also müssen Sie beide angeben. 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 Zuweisung zu signal wählt die gewünschte Überladung aus und kann nun erfolgreich in die Vorlage eingefügt 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 static_cast hier vermeiden, da es sich lediglich um einen Zwang handelt, anstatt den Schutz der Sprache zu entfernen. 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 erlaubt uns zu schreiben

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

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

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

wird besser aussehen. : \

7
Newlifer

Die oben genannten Lösungen funktionieren, aber ich habe dies mit einem Makro auf etwas andere Weise gelöst.

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

Fügen Sie dies in Ihren Code ein.

Dann dein Beispiel:

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

Wird:

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