Je ne parviens pas à comprendre la nouvelle syntaxe signal/emplacement (utilisation d'un pointeur sur une fonction membre) dans Qt 5, comme décrit dans Nouvelle syntaxe d'emplacement de signal . J'ai essayé de changer ceci:
QObject::connect(spinBox, SIGNAL(valueChanged(int)),
slider, SLOT(setValue(int));
pour ça:
QObject::connect(spinBox, &QSpinBox::valueChanged,
slider, &QSlider::setValue);
mais j'obtiens une erreur en essayant de le compiler:
erreur: pas de fonction correspondante pour l'appel à
QObject::connect(QSpinBox*&, <unresolved overloaded function type>, QSlider*&, void (QAbstractSlider::*)(int))
J'ai essayé avec clang et gcc sous Linux, tous deux avec -std=c++11
.
Qu'est-ce que je fais mal et comment puis-je résoudre le problème?
Le problème ici est qu’il existe deux signaux portant ce nom: QSpinBox::valueChanged(int)
et QSpinBox::valueChanged(QString)
. A partir de Qt 5.7, des fonctions d’aide sont fournies pour sélectionner la surcharge souhaitée. Vous pouvez ainsi écrire
connect(spinbox, qOverload<int>(&QSpinBox::valueChanged),
slider, &QSlider::setValue);
Pour Qt 5.6 et les versions antérieures, vous devez indiquer à Qt celui que vous voulez sélectionner en le convertissant dans le bon type:
connect(spinbox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
slider, &QSlider::setValue);
Je sais, c'est moche. Mais il n'y a pas moyen de contourner cela. La leçon d'aujourd'hui est la suivante: ne surchargez pas vos signaux et vos créneaux! _
Addendum: Ce qui est vraiment ennuyant à propos du casting, c'est que
void
(pour les signaux).Je me suis donc parfois retrouvé en utilisant cet extrait 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;
}
};
Usage:
connect(spinbox, SELECT<int>::OVERLOAD_OF(&QSpinBox::valueChanged), ...)
Personnellement, je trouve que ce n'est pas vraiment utile. Je m'attends à ce que ce problème disparaisse de lui-même lorsque Creator (ou votre IDE) insère automatiquement la distribution correcte lors de la réalisation automatique de l'opération de prise du PMF. Mais en attendant ...
Remarque: la syntaxe de connexion basée sur PMF ne nécessite pas C++ 11!
Addendum 2: dans Qt 5.7, des fonctions d'aide ont été ajoutées pour atténuer ce problème, sur le modèle de la solution de contournement décrite ci-dessus. L'assistant principal est qOverload
(vous avez également qConstOverload
et qNonConstOverload
).
Exemple d'utilisation (tiré de la documentation):
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)
Le message d'erreur est:
erreur: pas de fonction correspondante pour l'appel à
QObject::connect(QSpinBox*&, <unresolved overloaded function type>, QSlider*&, void (QAbstractSlider::*)(int))
La partie importante de ceci est la mention " type de fonction surchargé non résolu ". Le compilateur ne sait pas si vous voulez dire QSpinBox::valueChanged(int)
ou QSpinBox::valueChanged(QString)
.
Il existe une poignée de moyens pour résoudre la surcharge:
connect()
QObject::connect<void(QSpinBox::*)(int)>(spinBox, &QSpinBox::valueChanged,
slider, &QSlider::setValue);
Cela force connect()
à résoudre &QSpinBox::valueChanged
dans la surcharge qui prend une int
.
Si vous avez des surcharges non résolues pour l'argument de logement, vous devrez alors fournir le second argument de modèle à connect()
. Malheureusement, il n'y a pas de syntaxe à demander pour que le premier soit déduit, vous devez donc fournir les deux. C'est à ce moment que la deuxième approche peut aider:
void(QSpinBox::*signal)(int) = &QSpinBox::valueChanged;
QObject::connect(spinBox, signal,
slider, &QSlider::setValue);
L'affectation à signal
sélectionnera la surcharge souhaitée et elle peut maintenant être substituée avec succès dans le modèle. Cela fonctionne aussi bien avec l'argument «slot», et je le trouve moins lourd dans ce cas.
Nous pouvons éviter static_cast
ici, car il s'agit simplement d'une contrainte plutôt que de la suppression des protections de la langue. J'utilise quelque chose comme:
// Also useful for making the second and
// third arguments of ?: operator agree.
template<typename T, typename U> T&& coerce(U&& u) { return u; }
Cela nous permet d'écrire
QObject::connect(spinBox, coerce<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged),
slider, &QSlider::setValue);
En fait, vous pouvez simplement envelopper votre fente avec lambda et ceci:
connect(spinbox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
slider, &QSlider::setValue);
sera mieux paraître. : \
Les solutions ci-dessus fonctionnent, mais j’ai résolu le problème d’une manière légèrement différente, en utilisant une macro.
#define CONNECTCAST(OBJECT,TYPE,FUNC) static_cast<void(OBJECT::*)(TYPE)>(&OBJECT::FUNC)
Ajoutez ceci dans votre code.
Ensuite, votre exemple:
QObject::connect(spinBox, &QSpinBox::valueChanged,
slider, &QSlider::setValue);
Devient:
QObject::connect(spinBox, CONNECTCAST(QSpinBox, double, valueChanged),
slider, &QSlider::setValue);