Qt 5 में अतिभारित सिग्नल और स्लॉट को जोड़ना


133

मुझे Qt 5 में नए सिग्नल / स्लॉट सिंटैक्स (पॉइंटर से सदस्य फ़ंक्शन का उपयोग करके) के साथ पकड़ पाने में परेशानी हो रही है, जैसा कि न्यू सिग्नल स्लॉट सिंटैक्स में वर्णित है । मैंने इसे बदलने की कोशिश की:

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

इसके लिए:

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

लेकिन जब मैं इसे संकलित करने की कोशिश करता हूं तो मुझे एक त्रुटि मिलती है:

त्रुटि: कॉल करने के लिए कोई मेल नहीं खाता QObject::connect(QSpinBox*&, <unresolved overloaded function type>, QSlider*&, void (QAbstractSlider::*)(int))

मैंने लिनक्स पर क्लैंग और जीसीसी के साथ कोशिश की है, दोनों के साथ -std=c++11

मैं क्या गलत कर रहा हूं, और मैं इसे कैसे ठीक कर सकता हूं?


यदि आपका सिंटैक्स सही है, तो केवल स्पष्टीकरण यह हो सकता है कि आप Qt5 पुस्तकालयों से लिंक नहीं कर रहे हैं, लेकिन इसके बजाय Qt4। यह 'प्रोजेक्ट्स' पृष्ठ पर QtCreator के साथ सत्यापित करना आसान है।
मैट फिलिप्स

मैंने QObject (QSpinBox आदि) के कुछ उपवर्गों को शामिल किया, ताकि QObject को शामिल किया जाए। मैंने जोड़ने की कोशिश की, हालांकि इसमें भी शामिल है और यह अभी भी संकलन नहीं करेगा।
dtruby

इसके अलावा, मैं निश्चित रूप से क्यूटी 5 के खिलाफ लिंक कर रहा हूं, मैं क्यूटी क्रिएटर का उपयोग कर रहा हूं और दोनों किट जो मैं दोनों के साथ परीक्षण कर रहा हूं, क्यूटी 5.0.1 को उनके क्यूटी संस्करण के रूप में सूचीबद्ध किया गया है।
dtruby

जवाबों:


244

यहां समस्या यह है कि उस नाम के साथ दो संकेत हैं : QSpinBox::valueChanged(int)और QSpinBox::valueChanged(QString)। Qt 5.7 से, वांछित अधिभार का चयन करने के लिए सहायक कार्य हैं, जिससे आप लिख सकते हैं

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

Qt 5.6 और इससे पहले के लिए, आपको Qt बताने की जरूरत है, जिसे आप चुनना चाहते हैं, इसे सही प्रकार से कास्टिंग करके:

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

मुझे पता है, यह बदसूरत है । लेकिन इसके आसपास कोई रास्ता नहीं है। आज का सबक है: अपने सिग्नल और स्लॉट को ओवरलोड न करें!


परिशिष्ट : क्या वास्तव में कलाकारों के बारे में गुस्सा है

  1. एक वर्ग के नाम को दो बार दोहराता है
  2. भले ही यह आम तौर पर void(संकेतों के लिए) रिटर्न मान निर्दिष्ट करना हो ।

इसलिए मैंने खुद को कभी-कभी इस 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;
    } 
};

उपयोग:

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

मैं व्यक्तिगत रूप से इसे वास्तव में उपयोगी नहीं पाता हूं। मुझे उम्मीद है कि यह समस्या अपने आप दूर हो जाएगी जब क्रिएटर (या आपकी आईडीई) स्वचालित रूप से पीएमएफ लेने के संचालन को स्वत: पूर्ण करते समय सही डाली जाएगी। लेकिन इस बीच ...

नोट: PMF- आधारित कनेक्ट सिंटैक्स को C ++ 11 की आवश्यकता नहीं है !


परिशिष्ट 2 : Qt में 5.7 हेल्पर फ़ंक्शन को इसे कम करने के लिए जोड़ा गया था, जो मेरे वर्कअराउंड के बाद मॉडल किया गया था। मुख्य सहायक है qOverload(आपको भी मिल गया है qConstOverloadऔर qNonConstOverload)।

उपयोग उदाहरण (डॉक्स से):

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)

परिशिष्ट 3 : यदि आप किसी अतिभारित सिग्नल के प्रलेखन को देखते हैं, तो अब ओवरलोडिंग समस्या का समाधान डॉक्स में स्वयं स्पष्ट रूप से कहा गया है। उदाहरण के लिए, https://doc.qt.io/qt-5/qspinbox.html#valueChanged-1 कहता है

नोट: सिग्नल मान इस श्रेणी में ओवरलोड है। फ़ंक्शन पॉइंटर सिंटैक्स का उपयोग करके इस सिग्नल से कनेक्ट करने के लिए, क्यूटी फ़ंक्शन पॉइंटर प्राप्त करने के लिए एक सुविधाजनक सहायक प्रदान करता है जैसा कि इस उदाहरण में दिखाया गया है:

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

1
आह, यह बहुत समझ में आता है। मैं इस तरह के मामलों के लिए अनुमान लगाता हूं जहां सिग्नल / स्लॉट अतिभारित हैं मैं बस पुराने सिंटैक्स के साथ छड़ी करूंगा :-)। धन्यवाद!
dtruby

17
मैं नए वाक्यविन्यास के बारे में बहुत उत्साहित था ... अब ठंड निराशा की एक ठंड छप।
रशपाल 12

12
सोच रहे लोगों के लिए (मेरी तरह): "पीएमएफ" का अर्थ "पॉइंटर टू मेंबर फंक्शन" है।
विक्की चिजवानी

14
मैं व्यक्तिगत रूप static_castसे पुराने सिंटैक्स पर कुरूपता को पसंद करता हूं , सिर्फ इसलिए कि नया सिंटैक्स सिग्नल / स्लॉट के अस्तित्व के लिए एक संकलन-समय की जांच करने में सक्षम बनाता है जहां पुराना सिंटैक्स रनटाइम में विफल हो जाएगा।
विक्की चिजवानी

2
दुर्भाग्य से, सिग्नल को ओवरलोड नहीं करना अक्सर एक विकल्प नहीं होता है - क्यूटी अक्सर अपने स्वयं के सिग्नल को ओवरलोड करता है। (उदाहरण के लिए QSerialPort)
पायथन नॉट

14

त्रुटि संदेश है:

त्रुटि: कॉल करने के लिए कोई मेल नहीं खाता QObject::connect(QSpinBox*&, <unresolved overloaded function type>, QSlider*&, void (QAbstractSlider::*)(int))

इसका महत्वपूर्ण हिस्सा " अनसुलझे ओवरलोड फ़ंक्शन प्रकार " का उल्लेख है । संकलक को पता नहीं है कि आपका मतलब है QSpinBox::valueChanged(int)या नहीं QSpinBox::valueChanged(QString)

अधिभार को हल करने के कुछ मुट्ठी भर तरीके हैं:

  • के लिए एक उपयुक्त टेम्पलेट पैरामीटर प्रदान करें connect()

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

    यह उस अधिभार में connect()हल करने के लिए मजबूर करता &QSpinBox::valueChangedहै जो ए int

    यदि आपके पास स्लॉट तर्क के लिए अनसुलझे ओवरलोड हैं, तो आपको दूसरे टेम्पलेट तर्क को आपूर्ति करने की आवश्यकता होगी connect()। दुर्भाग्य से, पहले से अनुमान लगाने के लिए कोई सिंटैक्स नहीं है, इसलिए आपको दोनों की आपूर्ति करने की आवश्यकता होगी। जब दूसरा तरीका मदद कर सकता है:

  • सही प्रकार के एक अस्थायी चर का उपयोग करें

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

    असाइनमेंट signalवांछित अधिभार का चयन करेगा, और अब इसे टेम्पलेट में सफलतापूर्वक प्रतिस्थापित किया जा सकता है। यह 'स्लॉट' तर्क के साथ समान रूप से अच्छी तरह से काम करता है, और मुझे उस मामले में कम बोझिल लगता है।

  • रूपांतरण का उपयोग करें

    हम static_castयहां से बच सकते हैं, क्योंकि यह भाषा की सुरक्षा को हटाने के बजाय बस एक जबरदस्ती है। मैं कुछ का उपयोग करता हूं जैसे:

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

    यह हमें लिखने की अनुमति देता है

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

8

दरअसल, आप अपने स्लॉट को लैम्ब्डा और इस के साथ लपेट सकते हैं:

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

बेहतर लगेगा। : \


0

ऊपर दिए गए समाधान काम करते हैं, लेकिन मैंने मैक्रो का उपयोग करके इसे थोड़ा अलग तरीके से हल किया है, इसलिए बस यहां मामले में यह है:

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

इसे अपने कोड में जोड़ें।

फिर, आपका उदाहरण:

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

हो जाता है:

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

2
समाधान "ऊपर" क्या? यह मत समझो कि उत्तर वर्तमान में आप उन्हें देख रहे हैं उस क्रम में सभी को प्रस्तुत किया गया है!
टोबी स्पाइट

1
आप ओवरलोड के लिए इसका उपयोग कैसे करते हैं जो एक से अधिक तर्क लेते हैं? क्या अल्पविराम समस्याओं का कारण नहीं है? मुझे लगता है कि आपको वास्तव में पारेंस को पास करने की आवश्यकता है, अर्थात #define CONNECTCAST(class,fun,args) static_cast<void(class::*)args>(&class::fun)- CONNECTCAST(QSpinBox, valueChanged, (double))इस मामले में उपयोग किया जाता है।
टॉबी स्पीट

यह एक अच्छा उपयोगी मैक्रो है जब कई तर्कों के लिए कोष्ठक का उपयोग किया जाता है, जैसा कि टोबी की टिप्पणी में है
बेदखल कर दिया गया
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.