我有包装任意lambda并返回lambda结果的C++代码。
template <typename F>
auto wrapAndRun(F fn) -> decltype(F()) {
// foo();
auto result = fn();
// bar();
return result;
}除非F返回void (error: variable has incomplete type 'void'),否则这会起作用。我想过使用ScopeGuard来运行bar,但我不希望bar在fn抛出异常时运行。有什么想法吗?
另外,我后来发现有个叫a proposal to fix this inconsistency的人。
发布于 2017-12-28 04:53:16
另一个技巧可能是利用逗号运算符,例如:
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);
}发布于 2017-12-28 02:12:19
您可以编写一个简单的包装类来处理其中的这一部分:
template <class T>
struct CallAndStore {
template <class F>
CallAndStore(F f) : t(f()) {}
T t;
T get() { return std::forward<T>(t); }
};和专门化:
template <>
struct CallAndStore<void> {
template <class F>
CallAndStore(F f) { f(); }
void get() {}
};你可以通过一个小的工厂函数来提高可用性:
template <typename F>
auto makeCallAndStore(F&& f) -> CallAndStore<decltype(std::declval<F>()())> {
return {std::forward<F>(f)};
}那就用它吧。
template <typename F>
auto wrapAndRun(F fn) {
// foo();
auto&& result = makeCallAndStore(std::move(fn));
// bar();
return result.get();
}编辑:使用get中的std::forward强制转换,这似乎也可以正确地处理从函数返回引用。
发布于 2017-12-28 02:43:33
在这里,新的C++17 if constexpr添加可能会有所帮助。您可以选择是否在编译时返回fn()的结果:
#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;
}
}正如您所说的,C++2a也是一个选项,您还可以利用概念,对函数施加约束:
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;
}https://stackoverflow.com/questions/47996550
复制相似问题