C ++ 17:使用通用可变参数lambda包装可调用 [英] C++17: Wrapping callable using generic variadic lambda

查看:78
本文介绍了C ++ 17:使用通用可变参数lambda包装可调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I want to wrap a callable of any type (e.g. a lambda) transparently inside another callable to inject additional functionality. The wrapper's type should have the same characteristics as the original callable:

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

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

I attempted to use generic variadic lambdas as wrappers:

#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上的注释所表明的那样,包装可调用对象与原始可调用对象的调用方式不同.为了实现所需的功能需要更改什么?

As the comments on the static_asserts indicate, the wrapper callables are not invocable in the same way as the original callables. What needs to be changed in order to achieve the desired functionality?

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

The given example was compiled using Visual Studio 2017 (msvc 15.9.0).

推荐答案

这可能是MSVC的std::invoke_resultstd::is_invocable实现中的错误(即使使用Visual Studio 15.9.2,我也可以在此处重现该问题).您的代码可以与clang(libc ++)和gcc 正常工作,但我看不出任何原因不应该.但是,无论如何您实际上并不需要std::invoke_result,您可以让您的lambda推断出返回类型:

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). Your code works fine with clang (libc++) and gcc and I don't see any reason why it shouldn't. However, you don't really need std::invoke_result here anyways, you can just have your lambda deduce the return type:

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

然后也可以在MSVC上正常工作

wich then also seems to work fine with MSVC…

正如Piotr Skotnicki在下面的评论中所指出的, decltype(auto)将禁止SFINAE .要解决此问题,您可以改用尾随返回类型:

As pointed out by Piotr Skotnicki in the comments below, decltype(auto) will prohibit 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将进行更多输入,但是应该可以与SFINAE一起使用,并且也可以与MSVC一起使用

wich will be a bit more typing but should work fine with SFINAE and also seems to work fine with MSVC…

这篇关于C ++ 17:使用通用可变参数lambda包装可调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆