我想将任何类型的可调用对象(例如lambda)透明地包装在另一个可调用对象中,以注入其他功能。包装器的类型应具有与原始可调用者相同的特征:
我尝试使用通用可变参数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)进行编译。
答案 0 :(得分:7)
这可能是MSVC的std::invoke_result
或std::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)...);
};
}
wich将有更多输入,但是应该可以与SFINAE和also seems to work fine with MSVC一起使用...