#include <variant>
#include <exception>
#include <type_traits>
#include <cassert>
template <typename T>
struct Promise {
std::variant<
std::monostate,
std::conditional_t<std::is_void_v<T>, std::monostate, T>,
std::exception_ptr
> result_;
T await_resume() const {
assert(result_.index() > 0);
#if 1
// old code that I want to optimise
if (result_.index() == 2) {
std::rethrow_exception(std::get<2>(result_));
}
if constexpr (!std::is_void_v<T>) {
return std::get<1>(result_);
}
#else
// new code, won't compile
return std::visit([](auto&& arg) {
using TT = std::decay_t<decltype(arg)>;
if constexpr (!std::is_same_v<TT, std::exception_ptr>) {
std::rethrow_exception(arg);
} else if constexpr (!std::is_void_v<T>) {
return arg;
}
});
#endif
}
};
template int Promise<int>::await_resume() const;
template std::exception_ptr Promise<std::exception_ptr>::await_resume() const;
template void Promise<void>::await_resume() const;
Promise::await_resume是一个简单的函数,可完成以下操作:
std::exception_ptr
,请重新引发异常。T
(当T由用户设置时,也可能是std :: exception_ptr),请将其返回。如果T的类型为空,则不执行任何操作。最初,我使用.index()
检查和std::get
来实现它。它可以工作,但是std::get
东西会在内部生成额外的检查,而std::__1::__throw_bad_variant_access()
东西是不会发生的:https://godbolt.org/z/YnjxDy
我想根据cppreference使用std :: visit优化代码,但无法编译。
另一个问题是,当T的类型为std :: exception_ptr时,我怎么知道是否应该抛出它?
答案 0 :(得分:1)
visit
不会“优化”代码-这只是在variant
上进行匹配的好模式,并且对于确保您不会忘记任何类型特别有用。
但是visit
的要求之一是每个替代项都必须返回相同的类型。在您的用例中,这尤其成问题,因为应该只返回您的替代方案中的一个...因此,这不是一个很好的选择。您还需要处理monostate
中的visit
情况,您实际上没有办法(除了...抛出?),所以您只是在外面好运。
您之前使用的版本非常好,我只是在类型上加上一点注释以使其更具表现力:
struct Void { };
template <typename T>
struct Promise {
using Value = std::conditional_t<std::is_void_v<T>, Void, T>;
std::variant<
std::monostate,
Value,
std::exception_ptr
> result_;
T await_resume() const {
assert(not result_.valueless_by_exception());
assert(not std::holds_alternative<std::monostate>(result_));
if (auto* exc = std::get_if<std::exception_ptr>(&result)) {
std::rethrow_exception(*exc);
} else {
if constexpr (not std::is_void_v<T>) {
return std::get<T>(result_);
}
}
}
}
我认为这比显式使用0
,1
和2
更好。
另一个问题是,当
T
的类型为std::exception_ptr
时,我怎么知道是否应该扔掉它?
简单:您不会扔它。根据您的类型,通用代码中的语义不会有很大不同。如果Promise<T>::await_resume()
持有T
,则返回T
。 Promise<std::exception_ptr>::await_resume()
返回exception_ptr
。没关系。
我猜想实际上在上面的实现中,使用显式get_if<exception_ptr>
会变得模棱两可,这很不幸……所以也许0
/ 1
/ 2
是只是简单的方法。
答案 1 :(得分:0)
请参阅@Barry的答案,这是我的最终版本:
T await_resume() const {
if (auto* pep = std::get_if<2>(&result_)) {
std::rethrow_exception(*pep);
} else {
if constexpr (!std::is_void_v<T>) {
auto* pv = std::get_if<1>(&result_);
assert(pv);
return *pv;
}
}
}
生成完美的asm,无需额外的检查,无需bad_variant_access sh * t:https://godbolt.org/z/96gF_J