从成员函数签名中自动推导lambda参数

Automatically deduce lambda arguments from member function signature

本文关键字:lambda 参数 成员 函数      更新时间:2023-10-16

通常,我使用lambda函数为第三方库设置对成员函数的回调。例如:

setCallback([this](auto&& ...x) { handleCallback(std::forward<decltype(x)>(x)...); });

现在我有一个带有重载函数的库来设置回调,这使得这一行不明确:

#include <functional>
#include <iostream>
class Library
{
public:
typedef std::function<void (int)> IntFunction;
typedef std::function<void (int, int)> IntIntFunction;
void setCallback(IntFunction f) { f_int_ = f; }
void setCallback(IntIntFunction f) { f_int_int_ = f; }
void callCallback(int a, int b) {
if (f_int_) f_int_(a);
if (f_int_int_) f_int_int_(a, b);
}
private:
IntFunction f_int_;
IntIntFunction f_int_int_;
};
class MyClass
{
public:
MyClass() {
//lib.setCallback([this](auto&& ...x) { handleCallback(std::forward<decltype(x)>(x)...); });
lib.setCallback([this](int a, int b) { handleCallback(a, b); });
}
void handleCallback(int a, int b) {
std::cout << "handleCallback: " << a << ", " << b << std::endl;
}
Library lib;
};
int main()
{
MyClass myclass;
myclass.lib.callCallback(2, 3);
return 0;
}

有没有一种方法可以从handleCallback函数中自动推导出正确的参数,以避免lambda中的函数参数重复?

您可以为此创建函数:

class MyClass
{
template <typename Ret, typename ...Args>
void setCallback(Ret (MyClass::*member)(Args...) /*const*/) {
lib.setCallback([=](Args...args)
{
(this->*member)(std::forward<decltype(args)>(args)...);
});
}
public:
MyClass() { setCallback(&MyClass::handleCallback); }
void handleCallback(int a, int b) {
std::cout << "handleCallback: " << a << ", " << b << std::endl;
}
Library lib;
};

演示

诀窍是正确地提升函数,尤其是返回类型表达式,因为它将启用SFINAE,使std::function构造函数能够检测lambda是否能够被调用:

lib.setCallback([this](auto&& ...x) -> 
decltype(void(handleCallback(std::forward<decltype(x)>(x)...))) {
handleCallback(std::forward<decltype(x)>(x)...);
}
);

实例

您也可以转发无异常:

lib.setCallback([this](auto&& ...x)
noexcept(noexcept(handleCallback(std::forward<decltype(x)>(x)...)))
-> decltype(void(handleCallback(std::forward<decltype(x)>(x)...))) {
handleCallback(std::forward<decltype(x)>(x)...);
}
);

在这一点上,唯一明智的方法是使用宏:

#define RETURNS(...) noexcept(noexcept(__VA_ARGS__)) 
-> decltype((__VA_ARGS__)) {                         
return __VA_ARGS__;                              
}

并像这样使用:

lib.setCallback([this](auto&& ...x) RETURNS(handleCallback(std::forward<decltype(x)>(x)...)));

实例

根据给出的答案,我构建了这个函数:

#include <type_traits>
#include <utility>
template <typename TClass, typename TRet, typename ...Args>
auto getLambda(TClass *instance, TRet (TClass::*member)(Args...))
{
if constexpr (std::is_void<TRet>::value)
return [=](Args...args){ (instance->*member)(std::forward<decltype(args)>(args)...); };
else
return [=](Args...args){ return (instance->*member)(std::forward<decltype(args)>(args)...); };
}

它可以这样使用:

lib.setCallback(getLambda(this, &MyClass::handleCallback));

演示

现在我用它来替换我以前使用的lambda函数(如问题所示(,因为它提高了可读性。