Programming

Qt 5에서 과부하 신호 및 슬롯 연결

procodes 2020. 7. 21. 21:43
반응형

Qt 5에서 과부하 신호 및 슬롯 연결


New Signal Slot Syntax에 설명 된 것처럼 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))

Linux에서 clang 및 gcc를 모두 사용해 보았습니다 -std=c++11.

내가 뭘 잘못하고 있으며 어떻게 해결할 수 있습니까?


문제는 여기에 있다는 것입니다 : 그 이름을 가진 신호가 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를 수행하는 작업을 자동 완성 할 때 Creator (또는 IDE)가 올바른 캐스트를 자동으로 삽입하면이 문제가 저절로 사라질 것으로 기대합니다. 그러나 그 동안에 ...

참고 : 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

참고 :이 클래스에서는 Signal valueChanged가 오버로드됩니다. 함수 포인터 구문을 사용하여이 신호에 연결하기 위해 Qt는 다음 예제와 같이 함수 포인터를 얻는 편리한 도우미를 제공합니다.

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

오류 메시지는 다음과 같습니다.

오류 : 호출에 대한 일치 기능이 없습니다. QObject::connect(QSpinBox*&, <unresolved overloaded function type>, QSlider*&, void (QAbstractSlider::*)(int))

The important part of this is the mention of "unresolved overloaded function type". The compiler doesn't know whether you mean QSpinBox::valueChanged(int) or QSpinBox::valueChanged(QString).

There are a handful of ways to resolve the overload:

  • Provide a suitable template parameter to connect()

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

    This forces connect() to resolve &QSpinBox::valueChanged into the overload that takes an int.

    If you have unresolved overloads for the slot argument, then you'll need to supply the second template argument to connect(). Unfortunately, there's no syntax to ask for the first to be inferred, so you'll need to supply both. That's when the second approach can help:

  • Use a temporary variable of the correct type

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

    The assignment to signal will select the desired overload, and now it can be substituted successfully into the template. This works equally well with the 'slot' argument, and I find it less cumbersome in that case.

  • Use a conversion

    We can avoid static_cast here, as it's simply a coercion rather than removal of the language's protections. I use something like:

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

    This allows us to write

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

Actually, you can just wrap your slot with lambda and this:

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

will be look better. :\


The solutions above work, but I solved this in a slightly different way, using a macro, So just in case here it is:

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

Add this in your code.

Then, your example:

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

Becomes:

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

참고URL : https://stackoverflow.com/questions/16794695/connecting-overloaded-signals-and-slots-in-qt-5

반응형