Qt 5'te aşırı yüklenmiş sinyalleri ve yuvaları bağlama


133

Yeni Sinyal Yuvası Sözdizimi'nde açıklandığı gibi, Qt 5'te yeni sinyal / yuva sözdizimini (işaretçiden üyeye işlevini kullanarak) kavramakta güçlük çekiyorum . Bunu değiştirmeyi denedim:

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

buna:

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

ama derlemeye çalıştığımda bir hata alıyorum:

hata: çağrı için eşleşen işlev yok QObject::connect(QSpinBox*&, <unresolved overloaded function type>, QSlider*&, void (QAbstractSlider::*)(int))

Linux'ta clang ve gcc ile denedim, ikisi de -std=c++11.

Neyi yanlış yapıyorum ve bunu nasıl düzeltebilirim?


Sözdiziminiz doğruysa, o zaman tek açıklama Qt5 kitaplıklarına bağlanmadığınız olabilir, bunun yerine Qt4 gibi. Bunu, 'Projeler' sayfasında QtCreator ile doğrulamak kolaydır.
Matt Phillips

QObject'in (QSpinBox vb.) Bazı alt sınıflarını dahil ettim, böylece QObject'i içermesi gerekirdi. Bunu da dahil etmeyi denedim ve yine de derlenmeyecek.
dtruby

Ayrıca, Qt 5 ile kesinlikle bağlantı kuruyorum, Qt Creator kullanıyorum ve her ikisiyle de test ettiğim iki kit, Qt sürümleri olarak Qt 5.0.1 listelenmiş.
dtruby

Yanıtlar:


244

Buradaki sorun , bu isimde iki sinyalin olmasıdır: QSpinBox::valueChanged(int)ve QSpinBox::valueChanged(QString). Qt 5.7'den, istenen aşırı yüklemeyi seçmek için sağlanan yardımcı işlevler vardır, böylece yazabilirsiniz

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

Qt 5.6 ve öncesi için, Qt'ye hangisini seçmek istediğinizi doğru türe çevirerek söylemeniz gerekir:

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

Biliyorum, bu çirkin . Ama bunun etrafında hiçbir yolu yok. Bugünün dersi: sinyallerinizi ve yuvalarınızı aşırı yüklemeyin!


Ek : Oyuncularla ilgili gerçekten sinir bozucu olan şey,

  1. biri sınıf adını iki kez tekrarlar
  2. genellikle void(sinyaller için) olsa bile dönüş değerini belirtmek zorundadır .

Bu yüzden kendimi bazen bu C ++ 11 pasajını kullanırken buldum:

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

Kullanımı:

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

Ben şahsen bunu pek yararlı bulmuyorum. Oluşturucu (veya IDE'niz) PMF alma işlemini otomatik olarak tamamlarken doğru atamayı otomatik olarak ekleyeceği zaman bu sorunun kendiliğinden ortadan kalkmasını bekliyorum. Ama bu arada ...

Not: PMF tabanlı bağlantı sözdizimi C ++ 11 gerektirmez !


Ek 2 : Qt 5.7'de, yukarıdaki geçici çözümümden sonra modellenen, bunu azaltmak için yardımcı işlevler eklendi. Ana yardımcısıdır qOverload(ayrıca var qConstOverloadve qNonConstOverload).

Kullanım örneği (dokümanlardan):

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)

Ek 3 : Aşırı yüklenmiş herhangi bir sinyalin belgelerine bakarsanız, artık aşırı yükleme sorununun çözümü belgelerin kendisinde açıkça belirtilmiştir. Örneğin, https://doc.qt.io/qt-5/qspinbox.html#valueChanged-1 diyor ki

Not: Signal valueChanged bu sınıfta aşırı yüklenmiştir. İşlev işaretçisi sözdizimini kullanarak bu sinyale bağlanmak için Qt, bu örnekte gösterildiği gibi işlev işaretçisini elde etmek için uygun bir yardımcı sağlar:

   connect(spinBox, QOverload<const QString &>::of(&QSpinBox::valueChanged),
[=](const QString &text){ /* ... */ });

1
Ah evet, bu çok mantıklı. Sanırım sinyallerin / yuvaların aşırı yüklü olduğu bu gibi durumlarda eski sözdizimine sadık kalacağım :-). Teşekkürler!
dtruby

17
Yeni sözdizimi beni çok heyecanlandırdı ... şimdi de dondurucu bir hayal kırıklığı.
RushPL

12
Merak edenler için (benim gibi): "pmf", "üye işlevine işaretçi" anlamına gelir.
Vicky Chijwani

14
Ben şahsen static_castçirkinliği eski sözdizimine tercih ederim, çünkü yeni sözdizimi , eski sözdiziminin çalışma zamanında başarısız olacağı sinyal / yuvanın varlığının bir derleme zamanı kontrolünü mümkün kılar .
Vicky Chijwani

2
Ne yazık ki, bir sinyali aşırı yüklememek genellikle bir seçenek değildir - Qt genellikle kendi sinyallerini aşırı yükler. (örneğin QSerialPort)
PythonNut

14

Hata mesajı:

hata: çağrı için eşleşen işlev yok QObject::connect(QSpinBox*&, <unresolved overloaded function type>, QSlider*&, void (QAbstractSlider::*)(int))

Bunun önemli kısmı, " çözülmemiş aşırı yüklenmiş fonksiyon türü " nden bahsetmektir . Şunu ister derleyici bilmiyor QSpinBox::valueChanged(int)ya QSpinBox::valueChanged(QString).

Aşırı yüklemeyi çözmenin birkaç yolu vardır:

  • İçin uygun bir şablon parametresi sağlayın connect()

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

    Bu kuvvetler connect()çözmek için &QSpinBox::valueChangedbir sürer aşırı içine int.

    Slot argümanı için çözülmemiş aşırı yükleriniz varsa, ikinci şablon argümanını sağlamanız gerekir connect(). Ne yazık ki, ilkinin çıkarılmasını isteyecek bir sözdizimi yok, bu yüzden ikisini de sağlamanız gerekecek. İşte o zaman ikinci yaklaşım yardımcı olabilir:

  • Doğru türde geçici bir değişken kullanın

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

    Atama signal, istenen aşırı yüklemeyi seçecek ve şimdi şablona başarıyla yerleştirilebilir. Bu, 'yuva' argümanıyla eşit derecede iyi çalışıyor ve bu durumda onu daha az külfetli buluyorum.

  • Bir dönüşüm kullanın

    static_castDilin korumasının kaldırılması değil, basitçe bir zorlama olduğu için buradan kaçınabiliriz . Şöyle bir şey kullanıyorum:

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

    Bu yazmamızı sağlar

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

8

Aslında, yuvanızı lambda ile sarmalayabilirsiniz ve bu:

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

daha iyi görünecek. : \


0

Yukarıdaki çözümler işe yarıyor, ancak bunu biraz daha farklı bir şekilde, bir makro kullanarak çözdüm, Yani tam da burada:

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

Bunu kodunuza ekleyin.

Ardından, örneğiniz:

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

Oluyor:

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

2
Neyin "üstünde" çözümler? Cevapların şu anda gördüğünüz sırayla herkese sunulduğunu varsaymayın!
Toby Speight

1
Bunu birden fazla argüman gerektiren aşırı yüklemeler için nasıl kullanıyorsunuz? Virgül sorun yaratmaz mı? Sanırım gerçekten parantezleri geçmeniz gerekiyor, yani #define CONNECTCAST(class,fun,args) static_cast<void(class::*)args>(&class::fun)- CONNECTCAST(QSpinBox, valueChanged, (double))bu durumda olduğu gibi.
Toby Speight

Toby'nin yorumunda olduğu gibi, parantezler birden çok argüman için kullanıldığında güzel ve kullanışlı bir makro
ejectamenta
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.