bar*_*top 19 c++ templates c++14
我正在为将来的库编写一些通用代码。我在模板函数中遇到以下问题。考虑下面的代码:
template<class F>
auto foo(F &&f) {
auto result = std::forward<F>(f)(/*some args*/);
//do some generic stuff
return result;
}
Run Code Online (Sandbox Code Playgroud)
除非我将返回的函数传递给它,否则它将正常工作void:
foo([](){});
Run Code Online (Sandbox Code Playgroud)
现在,当然,我现在可以使用一些std::enable_if魔术来检查返回类型,并对返回的函数执行专门化的操作void,如下所示:
template<class F, class = /*enable if stuff*/>
void foo(F &&f) {
std::forward<F>(f)(/*some args*/);
//do some generic stuff
}
Run Code Online (Sandbox Code Playgroud)
但这将完全复制实际上逻辑上等效的功能的代码。能否以一种优雅的方式,以通用的方式轻松地对- void返回和非void返回功能同时进行?
编辑:f()我想做的功能和通用的东西之间有数据依赖关系,所以我不考虑这样的代码:
template<class F>
auto foo(F &&f) {
//do some generic stuff
return std::forward<F>(f)(/*some args*/);
}
Run Code Online (Sandbox Code Playgroud)
max*_*x66 15
如果您可以将“一些通用的东西”放在bar类的析构函数中(在安全性try / catch块中,如果不确定如Drax所指出的那样不会引发异常),则只需编写
template <typename F>
auto foo (F &&f)
{
bar b;
return std::forward<F>(f)(/*some args*/);
}
Run Code Online (Sandbox Code Playgroud)
因此,编译器compute f(/*some args*/),exec的析构函数b并返回计算值(或什么都不返回)。
可以看到return func();,func()函数返回的位置void是完全合法的。
某些地方的一些专业化是必要的。但是这里的目标是避免专门化函数本身。但是,您可以专门研究一个助手类。
已在gcc 9.1和上进行了测试-std=c++17。
#include <type_traits>
#include <iostream>
template<typename T>
struct return_value {
T val;
template<typename F, typename ...Args>
return_value(F &&f, Args && ...args)
: val{f(std::forward<Args>(args)...)}
{
}
T value() const
{
return val;
}
};
template<>
struct return_value<void> {
template<typename F, typename ...Args>
return_value(F &&f, Args && ...args)
{
f(std::forward<Args>(args)...);
}
void value() const
{
}
};
template<class F>
auto foo(F &&f)
{
return_value<decltype(std::declval<F &&>()(2, 4))> r{f, 2, 4};
// Something
return r.value();
}
int main()
{
foo( [](int a, int b) { return; });
std::cout << foo( [](int a, int b) { return a+b; }) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
我认为,执行此操作的最佳方法是实际更改调用可能无效的函数的方式。基本上,我们更改void返回的类,而返回某种类类型Void,出于所有意图和目的,该类类型是同一件事,并且没有用户真正关心。
struct Void { };
Run Code Online (Sandbox Code Playgroud)
我们需要做的就是包装调用。以下使用C ++ 17名称(std::invoke和std::invoke_result_t),但是它们都可以在C ++ 14中实现,而不必大惊小怪:
// normal case: R isn't void
template <typename F, typename... Args,
typename R = std::invoke_result_t<F, Args...>,
std::enable_if_t<!std::is_void<R>::value, int> = 0>
R invoke_void(F&& f, Args&&... args) {
return std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
}
// special case: R is void
template <typename F, typename... Args,
typename R = std::invoke_result_t<F, Args...>,
std::enable_if_t<std::is_void<R>::value, int> = 0>
Void invoke_void(F&& f, Args&&... args) {
// just call it, since it doesn't return anything
std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
// and return Void
return Void{};
}
Run Code Online (Sandbox Code Playgroud)
这样做的好处是,您可以直接以要编写的方式编写要编写的代码:
template<class F>
auto foo(F &&f) {
auto result = invoke_void(std::forward<F>(f), /*some args*/);
//do some generic stuff
return result;
}
Run Code Online (Sandbox Code Playgroud)
而且,您不必将所有逻辑推到析构函数中,也不必通过专门化来复制所有逻辑。以foo([]{})返回Void而不是的代价为代价void,这并不是很多费用。
然后如果经常虚空中不断采用,所有你需要做的就是换出invoke_void了std::invoke。
| 归档时间: |
|
| 查看次数: |
842 次 |
| 最近记录: |