C++17:使用通用可变参数 lambda 包装可调用对象

标签 c++ lambda wrapper c++17 perfect-forwarding

我想将任何类型的可调用对象(例如 lambda)透明地包装在另一个可调用对象中以注入(inject)额外的功能。包装器的类型应具有与原始可调用对象相同的特征:

  • 相同的参数类型
  • 相同的返回类型
  • 完美转发传递的参数
  • 在 SFINAE 结构中使用相同的行为

我尝试使用通用可变参数 lambda 作为包装器:

#include <iostream>
#include <type_traits>

template<class TCallable>
auto wrap(TCallable&& callable) {
    return [callable = std::forward<TCallable>(callable)](auto&&... args) -> std::invoke_result_t<TCallable,decltype(args)...> {
        std::cout << "This is some additional functionality" << std::endl;
        return callable(std::forward<decltype(args)>(args)...);
    };
}

int main(int argc, char *argv[])
{
    auto callable1 = []() {
        std::cout << "test1" << std::endl;
    };

    auto callable2 = [](int arg) {
        std::cout << "test2: " << arg << std::endl;
    };

    auto wrapped1 = wrap(callable1);
    auto wrapped2 = wrap(callable2);

    static_assert(std::is_invocable_v<decltype(callable1)>); // OK
    static_assert(std::is_invocable_v<decltype(wrapped1)>); // fails
    static_assert(std::is_invocable_v<decltype(callable2), int>); // OK
    static_assert(std::is_invocable_v<decltype(wrapped2), int>); // fails
}

static_assert 上的注释所示,包装器可调用对象的调用方式与原始可调用对象不同。需要更改什么才能实现所需的功能?

给定的示例是使用 Visual Studio 2017 (msvc 15.9.0) 编译的。

最佳答案

这可能是 MSVC 的 std::invoke_resultstd::is_invocable 实现中的错误(即使使用 Visual Studio 15.9.2,我也可以在此处重现该问题) .您的代码 works fine with clang (libc++) and gcc而且我看不出有任何不应该这样做的理由。然而,无论如何,你并不真的需要 std::invoke_result,你可以让你的 lambda 推导出返回类型:

template<class TCallable>
auto wrap(TCallable&& callable) {
    return [callable = std::forward<TCallable>(callable)](auto&&... args) -> decltype(auto) {
        std::cout << "This is some additional functionality" << std::endl;
        return callable(std::forward<decltype(args)>(args)...);
    };
}

然后also seems to work fine with MSVC ……

编辑:正如 Piotr Skotnicki 在下面的评论中指出的那样,decltype(auto) will prohibit SFINAE .要解决此问题,您可以改用尾随返回类型:

template<class TCallable>
auto wrap(TCallable&& callable) {
    return [callable = std::forward<TCallable>(callable)](auto&&... args) -> decltype(callable(std::forward<decltype(args)>(args)...)) {
        std::cout << "This is some additional functionality" << std::endl;
        return callable(std::forward<decltype(args)>(args)...);
    };
}

打字会多一些,但应该可以与 SFINAE 和 also seems to work fine with MSVC 一起正常工作……

关于C++17:使用通用可变参数 lambda 包装可调用对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53461019/

相关文章:

c++ - libshout 构建对 SSL_is_init_finished 的 undefined reference

c++ - 函数包装避免重复

ruby - `send` 替代 Ruby 中的 lambda

c# - 使包装类的扩展方法/构造函数通用

c++ - 使用 swig 包装一个大图书馆

c++ - 如何始终在 QDoubleSpinbox 中显示符号(+ 或 -)?

c++ - 具有 VARIANT 返回类型的模板实例化

c++ - Qt 不识别其他子项目

c++ - 不能使用显式类型的 lambda

c++ for_each 并行处理两个容器