简体   繁体   English

C ++ 17:使用泛型variadic lambda包装可调用

[英]C++17: Wrapping callable using generic variadic lambda

I want to wrap a callable of any type (eg a lambda) transparently inside another callable to inject additional functionality. 我想在另一个callable中透明地包装任何类型的可调用(例如lambda)以注入其他功能。 The wrapper's type should have the same characteristics as the original callable: 包装器的类型应该与原始可调用的类型相同:

  • Identical parameter types 相同的参数类型
  • Identical return type 相同的退货类型
  • Perfect forwarding of passed arguments 完美转发传递的参数
  • Same behaviour when used in SFINAE constructs 在SFINAE构造中使用时的行为相同

I attempted to use generic variadic lambdas as wrappers: 我试图使用泛型variadic lambdas作为包装:

#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
}

As the comments on the static_assert s indicate, the wrapper callables are not invocable in the same way as the original callables. 正如static_assert上的注释所示,包装器callables的调用方式与原始callable的调用方式不同。 What needs to be changed in order to achieve the desired functionality? 需要改变什么才能实现所需的功能?

The given example was compiled using Visual Studio 2017 (msvc 15.9.0). 给出的示例是使用Visual Studio 2017(msvc 15.9.0)编译的。

This is probably a bug in MSVC's implementation of std::invoke_result or std::is_invocable (I can reproduce the issue here even with Visual Studio 15.9.2). 这可能是MSVC实现std::invoke_resultstd::is_invocable一个错误(即使使用Visual Studio 15.9.2,我也可以在这里重现这个问题)。 Your code works fine with clang (libc++) and gcc and I don't see any reason why it shouldn't. 你的代码使用clang(libc ++)和gcc工作正常,我没有看到任何理由不应该这样做。 However, you don't really need std::invoke_result here anyways, you can just have your lambda deduce the return type: 但是,你真的不需要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)...);
    };
}

wich then also seems to work fine with MSVC 那么MSVC似乎也能正常工作 ......

Edit: As pointed out by Piotr Skotnicki in the comments below, decltype(auto) will prohibit SFINAE . 编辑:正如Piotr Skotnicki在下面的评论中所指出的, decltype(auto)将禁止SFINAE To solve this issue, you can use a trailing return type instead: 要解决此问题,您可以使用尾随返回类型:

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

wich will be a bit more typing but should work fine with SFINAE and also seems to work fine with MSVC 这将是一个更多的打字,但应该与SFINAE一起使用, 并且似乎也可以与MSVC一起使用 ...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM