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


133

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))

나는 리눅스에서 clang과 gcc를 사용해 보았습니다 -std=c++11.

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


구문이 맞다면 Qt5 라이브러리에 연결하지 않고 Qt4와 같이 연결해야한다는 것이 유일한 설명 일 수 있습니다. '프로젝트'페이지에서 QtCreator로 쉽게 확인할 수 있습니다.
매트 필립스

QObject가 포함되도록 QObject (QSpinBox 등)의 일부 서브 클래스를 포함 시켰습니다. 그래도 포함을 추가하려고 시도했지만 여전히 컴파일되지 않습니다.
dtruby

또한 Qt 5와 확실히 연결되어 있습니다 .Qt Creator를 사용하고 있으며 테스트중인 두 키트에는 Qt 5.0.1이 Qt 버전으로 표시되어 있습니다.
dtruby

답변:


244

문제는 여기에 있다는 것입니다 : 그 이름을 가진 신호가 QSpinBox::valueChanged(int)QSpinBox::valueChanged(QString). Qt 5.7부터 원하는 과부하를 선택하기 위해 제공되는 도우미 기능이 있으므로 작성할 수 있습니다

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

Qt 5.6 이하의 경우 Qt에게 올바른 유형으로 캐스팅하여 선택하려는 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){ /* ... */ });

1
아 맞아요. 신호 / 슬롯이 과부하 된 경우와 같은 경우에는 이전 구문을 그대로 사용합니다 :-). 감사!
dtruby

17
나는 새로운 문법에 대해 너무나 흥분했다.
RushPL

12
(나 같은) 궁금한 사람들을 위해 : "pmf"는 "멤버에 대한 포인터 기능"을 의미합니다.
Vicky Chijwani

14
개인적으로 static_cast이전 구문보다 추한 것을 선호 합니다. 단순히 새로운 구문으로 인해 런타임에 이전 구문이 실패하는 신호 / 슬롯이 있는지 컴파일 타임 검사 할 수 있기 때문 입니다.
Vicky Chijwani

2
불행하게도, 신호에 과부하를 걸지 않는 것은 종종 옵션이 아닙니다. Qt는 종종 자체 신호를 과부하시킵니다. (예 QSerialPort)
PythonNut

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은 원하는 과부하를 선택하고 이제 템플릿으로 대체 할 수 있습니다. 이것은 'slot'인수와 똑같이 잘 작동하며,이 경우 덜 성가신 것으로 나타났습니다.

  • 전환 사용

    우리는 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
솔루션 "위"무엇? 현재보고있는 순서대로 답변이 모든 사람에게 제공된다고 가정하지 마십시오!
Toby Speight

1
둘 이상의 인수를 취하는 과부하에 이것을 어떻게 사용합니까? 쉼표로 문제가 발생하지 않습니까? 나는 당신이 정말로이 경우 #define CONNECTCAST(class,fun,args) static_cast<void(class::*)args>(&class::fun)와 같이 사용되는 파 렌스를 전달해야한다고 생각합니다 CONNECTCAST(QSpinBox, valueChanged, (double)).
Toby Speight

그것은 괄호는 토비의 의견과 같이 여러 인수에 사용되는 때 좋은 유용한 매크로입니다
ejectamenta
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.