包装可能无效的可调用返回

Bai*_*ker 3 c++ closures c++17

假设我有一些包装函数,我想做一些设置,调用回调(并保存其结果),做一些清理,然后返回回调返回的内容:

#include <functional>
#include <utility>

template<class F, typename... Args>
decltype(auto) wrap(F&& func, Args&... args) {
    // Do some stuff before
    auto result = std::invoke(std::forward<decltype(func)>(func),
                              std::forward<Args>(args)...);
    // Do some stuff after
    return result;
}
Run Code Online (Sandbox Code Playgroud)

一个实际的例子是一个计时器实用程序函数,它返回函数调用的经过时间及其返回值(也许在元组中).

这样的函数适用于具有返回类型的callables:

void foo() {
    auto a = 1;
    wrap([](auto a) { return 1; }, a);
}
Run Code Online (Sandbox Code Playgroud)

但是使用void返回类型的可调用,在模板特化期间编译器抱怨auto result具有不完整类型void:

void foo() {
    auto a = 1;
    wrap([](auto a) {}, a);
}
Run Code Online (Sandbox Code Playgroud)

这当然是有道理的,因为虽然你可以return void(),但你不能将它存储在一个变量中.

我想wrap为两种类型的callables工作.我试图用std::function两个签名wrap:

  1. template<class T, typename... Args> decltype(auto) wrap(std::function<T(Args...)>, Args&... args)
  2. template<typename... Args> decltype(auto) wrap(std::function<void(Args...)>, Args&... args)

第一个将继续匹配不void返回的callables ,但后者无法匹配返回类型的callables void.

有没有办法在返回类型void和不可void调用的情况下进行包装工作?

Bar*_*rry 5

我喜欢解决这个问题的方法是定期无效.好吧,我们实际上没有适当的常规void,但我们可以制作我们自己的Void常规类型,有点像void.然后提供一个invoke了解它的包装器.

最短的版本(该博客中有更多细节):

struct Void { };

template <typename F, typename ...Args,
    typename Result = std::invoke_result_t<F, Args...>,
    std::enable_if_t<!std::is_void_v<Result>, int> = 0>
Result invoke_void(F&& f, Args&& ...args) {
    return std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
}

// void case
template <typename F, typename ...Args,
    typename Result = std::invoke_result_t<F, Args...>,
    std::enable_if_t<std::is_void_v<Result>, int> = 0>
Void invoke_void(F&& f, Args&& ...args) {
    std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
    return Void();
}
Run Code Online (Sandbox Code Playgroud)

这使您可以在原始代码中执行以下操作:

template<class F, typename... Args>
decltype(auto) wrap(F&& func, Args&... args) {
    // Do some stuff before
    auto result = invoke_void(std::forward<decltype(func)>(func),
                              std::forward<Args>(args)...);
    // Do some stuff after
    return result;
}
Run Code Online (Sandbox Code Playgroud)

即使func返回,这也有效void.