信号槽的语法糖

Syntax sugar for signal slot

最近我在互联网上搜索了良好的信号槽库,想知道为什么我们在地球上需要如此繁琐的语法来将成员方法连接到信号?

通常我们要这样写:

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

或类似的:

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

有明显的重复和不必要的打字。是否有可能在现代 C++ 中以 C# 方式实现此类功能:

mySignal += myMethodName

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

Is it possible in modern C++ to implement such functionality in, for example, C# way? [...]

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

如果您不想指定 class 名称,一种可能是使用 lambda。特别是,如果您买得起 C++14 编译器,则通用 lambda 允许编写:

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

如果您不介意宏(我通常很介意),您可以使用基于预处理器的解决方案。但是,在这种情况下,我更愿意使用完美转发的 lambda:

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

你可以这样使用:

e.connect(SLOT(foo));

这里是live demo on Coliru.

预处理器可以尝试一下吗?

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

甘蔗用法如下:

mySignal.CONNECT(myMethodName);