C ++ 17:使用通用可变参数lambda包装可调用 [英] C++17: Wrapping callable using generic variadic 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_assert
s 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_result
或std::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)...);
};
}
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屋!