信号槽的语法糖

时间:2015-02-08 19:25:29

标签: c++ c++11 signals-slots

最近我在互联网上搜索了一个好的信号槽库并想知道为什么在地球上我们需要这么麻烦的语法来将成员方法连接到信号?

通常我们必须写这样的东西:

mySignal.connect(&MyClassName::myMethodName, this);

或者那样:

mySignal += std::bind(&MyClassName::myMethodName, this, std::placeholders::_1);

有明显的重复和无意识的打字。在现代C ++中是否有可能在例如C#方式中实现这样的功能:

mySignal += myMethodName

并自动捕获指向成员函数的指针和来自上下文的指针?

2 个答案:

答案 0 :(得分:6)

  

现代C ++中是否有可能以C#方式实现此类功能? [...]

不,这在C ++中是不可能的。获取成员函数地址的语法要求使用类名限定函数名称(即&MyClassName::myMethodName)。

如果您不想指定类名,可以使用lambdas。特别是,如果你能负担得起C ++ 14编译器,通用lambdas允许编写:

mySignal.connect([this] (auto x) -> { myMethodName(x) });
可悲的是,你不能比这更加苛刻。您可以使用默认的lambda捕获来保存一些语法噪音:

mySignal.connect([&] (auto x) -> { myMethodName(x) });

然而,Scott Meyers在他的新书 Effective Modern C ++ 中警告默认lambda捕获模式的缺陷。从可读性的角度来看,与您的第一个选项相比,我不确定这会改善很多事情。

此外,如果你希望你的lambda将其论点完美地转发给myMethodName,事情很快就会变得尴尬:

mySignal.connect([&] (auto&& x) -> { myMethodName(std::forward<decltype(x)>(x)) });

如果您不介意使用宏(我通常这样做),您可以使用基于预处理器的解决方案as suggested by Quentin in their answer。但是,在这种情况下,我更倾向于使用完美转发lambda:

#define SLOT(name) \
        [this] (auto&&... args) { name (std::forward<decltype(args)>(args)...); }

您可以这样使用:

e.connect(SLOT(foo));

这是live demo on Coliru

答案 1 :(得分:2)

预处理器应该对它有所了解吗?

#include <type_traits>
#define CONNECT(method) \
    connect(&std::remove_pointer_t<decltype(this)>::method, this)

甘蔗使用如下:

mySignal.CONNECT(myMethodName);