C++ std::visit和std::variant用法 #包括 #包括 #包括 #包括 模板 结构承诺{ std::变体< std::单态, 标准::有条件的, std::异常\u ptr >结果!; T等待(u resume)const{ 断言(结果索引()>0); #如果1 //我想优化的旧代码 如果(结果索引()==2){ std::rethrow_异常(std::get(result)); } 如果constexpr(!std::is_void_v){ 返回std::get(结果); } #否则 //新代码,无法编译 return std::visit([](自动和参数化){ 使用TT=std::Decation\u t; 如果constexpr(!std::相同){ std::重试异常(arg); }否则,如果constexpr(!std::is\u void\u v){ 返回arg; } }); #恩迪夫 } }; 模板int Promise::wait_resume()常量; 模板std::exception_ptr Promise::Wait_resume()常量; 模板void Promise::wait_resume()常量;
是一个简单的函数,它执行以下操作:C++ std::visit和std::variant用法 #包括 #包括 #包括 #包括 模板 结构承诺{ std::变体< std::单态, 标准::有条件的, std::异常\u ptr >结果!; T等待(u resume)const{ 断言(结果索引()>0); #如果1 //我想优化的旧代码 如果(结果索引()==2){ std::rethrow_异常(std::get(result)); } 如果constexpr(!std::is_void_v){ 返回std::get(结果); } #否则 //新代码,无法编译 return std::visit([](自动和参数化){ 使用TT=std::Decation\u t; 如果constexpr(!std::相同){ std::重试异常(arg); }否则,如果constexpr(!std::is\u void\u v){ 返回arg; } }); #恩迪夫 } }; 模板int Promise::wait_resume()常量; 模板std::exception_ptr Promise::Wait_resume()常量; 模板void Promise::wait_resume()常量;,c++,c++17,std-variant,C++,C++17,Std Variant,是一个简单的函数,它执行以下操作: 如果变量的值为std::exception_ptr,请重新显示异常 如果变量的值为T(T由用户设置,也可能是std::exception_ptr),则将其返回。如果T的类型为void,则不执行任何操作 最初我使用.index()检查和std::get实现它。它可以工作,但是std::getthings会在内部生成额外的检查,并且std::\uu 1::\uu抛出\u坏的\u变量\u访问() 我想根据使用std::visit优化代码,但无法编译它 另一个问题是,
std::exception_ptr
,请重新显示异常T
(T由用户设置,也可能是std::exception_ptr),则将其返回。如果T的类型为void,则不执行任何操作.index()
检查和std::get
实现它。它可以工作,但是std::get
things会在内部生成额外的检查,并且std::\uu 1::\uu抛出\u坏的\u变量\u访问()
我想根据使用std::visit优化代码,但无法编译它
另一个问题是,当T的类型是std::exception\u ptr时,我如何知道是否应该抛出它?visit
不会“优化”代码-它只是一个很好的模式,用于匹配变量,并且它特别有用,可以确保您不会忘记任何类型
但是,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_);
}
}
}
}
struct Void{};
模板
结构承诺{
使用Value=std::conditional\u t;
std::变体<
std::单态,
价值
std::异常\u ptr
>结果!;
T等待(u resume)const{
断言(不是结果\无值\被\异常()断言);
assert(不是std::holds_alternative(result_));
if(auto*exc=std::get_if(&result)){
std::重新提交例外(*exc);
}否则{
如果constexpr(不是std::is\u void\u v){
返回std::get(结果);
}
}
}
}
我认为这比显式使用0
、1
和2
要好一点
另一个问题是,当T
的类型是std::exception\u ptr
时,我如何知道是否应该抛出它
很简单:你不能扔它。在基于您的类型的泛型代码中,不要有完全不同的语义Promise::wait_resume()
如果包含T
,则返回T
<代码>承诺::等待恢复()
返回一个异常\u ptr
。那很好
我想,实际上,在我上面的实现中,如果使用显式的get\u if
,就会变得模棱两可,这是不幸的。。。因此,也许0
/1
/2
是最简单的方法。指的是@Barry的答案,这是我的最终版本:
T wait_resume()常量{
if(auto*pep=std::get_if(&result_)){
标准::重新提交例外(*pep);
}否则{
如果constexpr(!std::is_void_v){
auto*pv=std::get_if(&result_);
断言(pv);
返回*pv;
}
}
}
生成完美的asm,无额外检查,无错误的变量访问sh*t:如果与std::vector::operator[]不匹配,则std::variant
是否具有与get\u或ub\u行为相同的方法?>但访问的一个要求是每个备选方法都必须返回相同的类型。我可以让他们返回相同的类型。只需在rethrow\u异常
之后写入return T()
。由于rethrow_exception
声明为no_return
,因此rethrow_exception
之后的代码不应生成任何代码。但它仍然无法编译。@CarterLi 1)No.2)返回t()代码>只有在您的类型是默认可构造的情况下才有帮助,而且arg
可以是T
或monostate
,所以这仍然是不同的类型。1)似乎*get_if(&variant)
符合我的要求。由于取消对nullptr的引用是UB,编译器将删除返回nullptr的代码路径,包括if语句。