复杂的“计算器”当前是这样调用的:
result = calculator.calc(a, b, extra);
不幸的是,有时可能会因为服务器上发生的不相关的更改而失败。与不同部门同步很困难——重试更容易。
为此,我实现了一个retry
lambda 函数:
auto retry = [](const auto& func, auto... args) {
int attempts = 5;
for (;;) try {
return func(args...);
}
catch (const std::system_error& e) {
/* Don't retry in case of a system_error */
throw;
}
catch (const exception& e) {
if (attempts-- == 0)
throw; /* Give up and rethrow */
Logging::log(Logging::Level::Warning,
"%s. Retrying %d more times.", e.what(), attempts);
sleep(2);
}
};
上面的代码可以编译,但是,当我尝试使用它时:
result = retry(calculator.calc, a, b, extra);
我收到一个来自 clang 的错误:
必须调用对非静态成员函数的引用
GNU c++ 使用以下标记标记同一行:
非静态成员函数的无效使用
确实,这calc()
是该类的非静态方法Calculator
。
我该如何使用我的新 lambda?
在 lambda 中,将其替换
func(args...)
为std::invoke(func, args...)
。现在,除了函数指针/引用、lambda 和其他函数对象之外,您的 lambda 还支持成员函数指针作为可调用对象。传递成员函数指针的语法是:
其中
Calculator
是 的类类型calculator
。需要额外的参数&calculator
来调用类的指定对象/实例上的非静态成员函数。std::invoke()
知道如何自动处理它。这里你需要小心一点。因为你按值传递参数,如果你写
calculator
而不是 ,你会意外地在 的副本&calculator
上调用成员函数。calculator
一般来说,通过转发引用传递参数会更好:
然后你也可以这样调用它:
std::invoke()
知道在调用成员函数指针时如何处理指针和对类实例的引用。但请注意,由于您可能会
func()
多次调用,因此您不应std::forward
将引用转发到调用中。另外,顺便提一下,如果你想保持
retry
通用性,仅仅捕获是不够的std::exception
。没有要求必须从派生异常std::exception
。这只是标准库中使用的惯例,有时也被其他库使用。所有其他异常的后备
catch
块应如下所示:您可以直接传递 lambda: