我最近关注了Reddit的讨论,该讨论对整个编译器的std::visit
优化进行了很好的比较。我注意到以下内容:https://godbolt.org/z/D2Q5ED
当所有类型都满足某些条件时,GCC9和Clang9(我猜它们共享相同的stdlib)都不会生成用于检查并抛出无值异常的代码。这会导致更好的代码生成方式,因此我提出了MSVC STL的问题,并向其提供了以下代码:
template <class T>
struct valueless_hack {
struct tag {};
operator T() const { throw tag{}; }
};
template<class First,class... Rest>
void make_valueless(std::variant<First,Rest...>& v) {
try { v.emplace<0>(valueless_hack<First>()); }
catch(typename valueless_hack<First>::tag const&) {}
}
声称,这使得任何变体都没有价值,并阅读docu,它应该:
首先,销毁当前包含的值(如果有)。然后 直接初始化包含的值,就像构造一个值 输入
T_I
和参数std::forward<Args>(args)....
,如果 抛出异常,*this
可能会变成valueless_by_exception。
我不明白的原因:为什么将其表示为“可以”?如果整个操作失败,保持旧状态合法吗?因为这是GCC的工作:
// For suitably-small,trivially copyable types we can create temporaries
// on the stack and then memcpy them into place.
template<typename _Tp>
struct _Never_valueless_alt
: __and_<bool_constant<sizeof(_Tp) <= 256>,is_trivially_copyable<_Tp>>
{ };
然后(有条件地)执行以下操作:
T tmp = forward(args...);
reset();
construct(tmp);
// Or
variant tmp(inplace_index<I>,forward(args...));
*this = move(tmp);
因此,它基本上会创建一个临时文件,如果成功,则将其复制/移动到真实位置。
IMO违反了文档规定的“首先,破坏当前包含的值”。在我阅读标准时,在v.emplace(...)
之后,变量中的当前值总是被破坏,而新类型要么是set类型,要么是无值。
我确实知道条件is_trivially_copyable
排除了具有可观察的析构函数的所有类型。因此,这也可能是这样的:“如果变量以旧值重新初始化”。但是变体的状态是可观察到的效果。标准确实允许emplace
不更改当前值吗?
根据标准报价进行编辑:
然后初始化包含的值,就像使用参数
std::forward<Args>(args)...
直接对TI类型的值进行直接非列表初始化一样。
T tmp {std::forward<Args>(args)...}; this->value = std::move(tmp);
真的算作上述的有效实现吗?这是“好像”的意思吗?