C++ 在 Qt 5 中连接过载的信号和插槽

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16794695/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 20:39:37  来源:igfitidea点击:

Connecting overloaded signals and slots in Qt 5

c++qtqt5

提问by dtruby

I'm having trouble getting to grips with the new signal/slot syntax (using pointer to member function) in Qt 5, as described in New Signal Slot Syntax. I tried changing this:

我无法掌握 Qt 5 中的新信号/槽语法(使用指向成员函数的指针),如新信号槽语法中所述。我试着改变这个:

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

to this:

对此:

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

but I get an error when I try to compile it:

但是当我尝试编译它时出现错误:

error: no matching function for call to QObject::connect(QSpinBox*&, <unresolved overloaded function type>, QSlider*&, void (QAbstractSlider::*)(int))

错误:没有匹配的调用函数 QObject::connect(QSpinBox*&, <unresolved overloaded function type>, QSlider*&, void (QAbstractSlider::*)(int))

I've tried with clang and gcc on Linux, both with -std=c++11.

我试过在 Linux 上使用 clang 和 gcc,两者都使用-std=c++11.

What am I doing wrong, and how can I fix it?

我做错了什么,我该如何解决?

回答by peppe

The problem here is that there are twosignals with that name: QSpinBox::valueChanged(int)and QSpinBox::valueChanged(QString). From Qt 5.7, there are helper functions provided to select the desired overload, so you can write

这里的问题是有两个具有该名称的信号:QSpinBox::valueChanged(int)QSpinBox::valueChanged(QString)。从 Qt 5.7 开始,提供了帮助函数来选择所需的重载,因此您可以编写

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

For Qt 5.6 and earlier, you need to tell Qt which one you want to pick, by casting it to the right type:

对于 Qt 5.6 及更早版本,您需要通过将其转换为正确的类型来告诉 Qt 您想要选择哪一个:

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

I know, it's ugly. But there's no way around this. Today's lesson is: do not overload your signals and slots!

我知道,这很丑陋。但是没有办法解决这个问题。今天的教训是:不要让信号和槽过载!



Addendum: what's really annoying about the cast is that

附录:演员阵容真正令人讨厌的是

  1. one repeats the class name twice
  2. one has to specify the return value even if it's usually void(for signals).
  1. 一个重复两次类名
  2. 即使通常是void(对于信号),也必须指定返回值。

So I've found myself sometimes using this C++11 snippet:

所以我发现自己有时会使用这个 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), ...)

I personally find it not really useful. I expect this problem to go away by itself when Creator (or your IDE) will automatically insert the right cast when autocompleting the operation of taking the PMF. But in the meanwhile...

个人觉得用处不大。我希望当创建者(或您的 IDE)在自动完成获取 PMF 的操作时自动插入正确的演员表时,这个问题会自行消失。但同时...

Note: the PMF-based connect syntax does not require C++11!

注意:基于 PMF 的连接语法不需要 C++11



Addendum 2: in Qt 5.7 helper functions were added to mitigate this, modelled after my workaround above. The main helper is qOverload(you've also got qConstOverloadand qNonConstOverload).

附录 2:在 Qt 5.7 中添加了辅助函数来缓解这种情况,以我上面的解决方法为模型。主要的帮手是qOverload(你也有qConstOverloadqNonConstOverload)。

Usage example (from the docs):

使用示例(来自文档):

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)


Addendum 3: if you look at the documentation of any overloaded signal, now the solution to the overloading problem is clearly stated in the docs themselves. For instance, https://doc.qt.io/qt-5/qspinbox.html#valueChanged-1says

附录 3:如果您查看任何重载信号的文档,现在文档本身清楚地说明了重载问题的解决方案。例如,https: //doc.qt.io/qt-5/qspinbox.html#valueChanged-1

Note: Signal valueChanged is overloaded in this class. To connect to this signal by using the function pointer syntax, Qt provides a convenient helper for obtaining the function pointer as shown in this example:

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

注意:信号 valueChanged 在此类中被重载。为了使用函数指针语法连接到这个信号,Qt 提供了一个方便的助手来获取函数指针,如下例所示:

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

回答by Toby Speight

The error message is:

错误信息是:

error: no matching function for call to QObject::connect(QSpinBox*&, <unresolved overloaded function type>, QSlider*&, void (QAbstractSlider::*)(int))

错误:没有匹配的调用函数 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).

其中重要的部分是提到“未解析的重载函数类型”。编译器不知道你是否是说QSpinBox::valueChanged(int)还是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::valueChangedinto 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 signalwill 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_casthere, 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);
    
  • 提供合适的模板参数 connect()

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

    这会强制connect()解析&QSpinBox::valueChanged为采用int.

    如果 slot 参数有未解决的重载,则需要将第二个模板参数提供给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);
    

回答by Newlifer

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

实际上,你可以用 lambda 和这个来包装你的插槽:

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

will be look better. :\

会更好看。:\

回答by Basile Perrenoud

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