Handling void assignment in C++ generic programming
1920 观看
6回复
378 作者的声誉
I have C++ code that wraps an arbitrary lambda and returns the result of the lambda.
template <typename F>
auto wrapAndRun(F fn) -> decltype(F()) {
// foo();
auto result = fn();
// bar();
return result;
}
This works unless F
returns void
(error: variable has incomplete type 'void'
). I thought of using a ScopeGuard
to run bar
, but I don't want bar
to run if fn
throws. Any ideas?
回应 6
0像
34747 作者的声誉
This is pretty awkward and not extendable solution but very short and simple and it supports RVO which could be significant for some return types:
template <typename F>
auto wrapAndRun(F fn) -> decltype(F()) {
// foo();
char c;
auto runner = std::unique_ptr<char, decltype(bar)>( &c, bar );
try {
return fn();
}
catch( ... ) {
runner.release();
throw;
}
}
作者: Slava
发布者: 2017 年 12 月 27 日
22像
13352 作者的声誉
You can write a simple wrapper class that handles this part of it:
template <class T>
struct CallAndStore {
template <class F>
CallAndStore(F f) : t(f()) {}
T t;
T get() { return std::forward<T>(t); }
};
And specialize:
template <>
struct CallAndStore<void> {
template <class F>
CallAndStore(F f) { f(); }
void get() {}
};
You can improve usability with a small factory function:
template <typename F>
auto makeCallAndStore(F&& f) -> CallAndStore<decltype(std::declval<F>()())> {
return {std::forward<F>(f)};
}
Then use it.
template <typename F>
auto wrapAndRun(F fn) {
// foo();
auto&& result = makeCallAndStore(std::move(fn));
// bar();
return result.get();
}
Edit: with the std::forward
cast inside get
, this also seems to handle returning a reference from a function correctly.
4像
41468 作者的声誉
Solution with SFINAE, the idea is to make internally a void function actually return int - hopefully the compiler will optimize this away. On outside wrapAndRun
will return the same type as a wrapped function.
http://coliru.stacked-crooked.com/a/e84ff8f74b3b6051
#include <iostream>
template <typename F>
auto wrapAndRun1(F fn) -> std::enable_if_t<!std::is_same_v<std::result_of_t<F()>, void>, std::result_of_t<F()>> {
return fn();
}
template <typename F>
auto wrapAndRun1(F fn) -> std::enable_if_t<std::is_same_v<std::result_of_t<F()>, void>, int> {
fn();
return 0;
}
template <typename F>
auto wrapAndRun(F fn) -> std::result_of_t<F()> {
// foo();
[[maybe_unused]] auto result = wrapAndRun1(fn);
// bar();
if constexpr (std::is_void_v<std::result_of_t<F()>>)
return;
else
return result;
}
int main()
{
wrapAndRun([] { std::cout << "with return \n"; return 0; });
wrapAndRun([] { std::cout << "no return \n"; });
}
作者: marcinj
发布者: 2017 年 12 月 27 日
13像
2156 作者的声誉
The new C++17 if constexpr
addition may be helpful here. You can choose whether to return fn()
's result at compile-time:
#include <type_traits>
template <typename F>
auto wrapAndRun(F fn) -> decltype(fn())
{
if constexpr (std::is_same_v<decltype(fn()), void>)
{
foo();
fn();
bar();
}
else
{
foo();
auto result = fn();
bar();
return result;
}
}
As you said C++2a is an option as well, you could also make use of concepts, putting a constraint on the function:
template <typename F>
requires requires (F fn) { { fn() } -> void }
void wrapAndRun(F fn)
{
foo();
fn();
bar();
}
template <typename F>
decltype(auto) wrapAndRun(F fn)
{
foo();
auto result = fn();
bar();
return result;
}
作者: Mário Feroldi
发布者: 2017 年 12 月 27 日
5像
5046 作者的声誉
Another trick might be to exploit the comma operator, something like:
struct or_void {};
template<typename T>
T&& operator,( T&& x, or_void ){ return std::forward<T>(x); }
template <typename F>
auto wrapAndRun(F fn) -> decltype(fn()) {
// foo();
auto result = ( fn(), or_void() );
// bar();
return decltype(fn())(result);
}
作者: Massimiliano Janes
发布者: 2017 年 12 月 27 日
5像
298 作者的声誉
You can have a look at the ScopeGuard by Alexandrescu: ScopeGuard.h It executes code only when there was no exception.
template<class Fn>
decltype(auto) wrapAndRun(Fn&& f) {
foo();
SCOPE_SUCCESS{ bar(); }; //Only executed at scope exit when there are no exceptions.
return std::forward<Fn>(f)();
}
So in case of no errors, the execution order is: 1. foo(), 2. f(), 3. bar(). And in case of an exception the order is: 1. foo(), 2. f()
作者: Kilian 发布者: 2017 年 12 月 27 日来自类别的问题 :
- c++ 理解指针有什么障碍,克服它们可以做些什么?
- c++ 在C ++中解析INI文件的最简单方法是什么?
- c++ 什么时候应该在C ++中使用“朋友”?
- c++ 你如何清除stringstream变量?
- c++ 在C ++中,构造函数和析构函数可以是内联函数吗?
- c++ 在C ++中,什么是虚拟基类?
- c++ 什么是C / C ++程序及其插件DLL的最佳免费内存泄漏检测器?
- c++ 如何在C ++(Unicode)中将std :: string转换为LPCWSTR
- c++ 常规投放与static_cast与dynamic_cast
- c++ C ++强制转换语法样式
- c++ 如何从Windows注册表中读取值
- c++ 哪些C ++编译器进行尾递归优化?
- c++ C ++和Java中“泛型”类型之间有什么区别?
- c++ 如何将反射添加到C ++应用程序?
- c++ 如何在c ++中调用:: CreateProcess来启动Windows可执行文件?
- c++ 为什么库链接的顺序有时会导致GCC错误?
- c++ How do you detect/avoid Memory leaks in your (Unmanaged) code?
- c++ 你如何设置,清除和切换一个位?
- c++ 从c ++程序调用python进行分发
- c++ 如何从C ++中的字符串获取文件扩展名