我最近关注了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&) {}
}
声称是,这使得任何变体都没有价值,并且阅读文档应该:
首先,销毁当前包含的值(如果有)。然后直接初始化包含的值,就像
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(...)
在变体中的当前值总是被销毁之后,新类型要么是集合类型,要么是无值类型。
我确实知道条件is_trivially_copyable
排除了所有具有可观察析构函数的类型。因此,这也可能是这样的:“如果变量以旧值重新初始化”。但是变体的状态是可观察到的效果。那么,标准确实允许emplace
不改变当前值吗?
根据标准报价进行编辑:
然后初始化包含的值,就好像直接使用参数初始化TI类型的值一样
std::forward<Args>(args)...
。
难道T tmp {std::forward<Args>(args)...}; this->value = std::move(tmp);
真的算作以上的有效实施?这是“好像”的意思吗?
might/may
措辞感到非常困惑,因为该标准并未说明替代方案。