首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >GCC9是否允许避免std::变体的无价值状态?

GCC9是否允许避免std::变体的无价值状态?
EN

Stack Overflow用户
提问于 2019-11-13 09:54:09
回答 1查看 568关注 0票数 15

最近,我关注了Reddit的一次讨论,该讨论导致在编译器之间对std::visit优化进行了很好的比较。我注意到以下几点:https://godbolt.org/z/D2Q5ED

当所有类型都满足某些条件时,GCC9和Clang9 (我猜它们共享相同的stdlib)都不会生成用于检查和抛出无值异常的代码。这导致了更好的代码生成,因此我向MSVC STL提出了一个问题,并给出了以下代码:

代码语言:javascript
复制
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就是这样做的:

代码语言:javascript
复制
  // 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>>
    { };

后来,它(有条件地)做了如下的事情:

代码语言:javascript
复制
T tmp  = forward(args...);
reset();
construct(tmp);
// Or
variant tmp(inplace_index<I>, forward(args...));
*this = move(tmp);

因此,它基本上创建了一个临时的,如果成功的话,复制/移动它到真正的地方。

海事组织这是违反“第一,摧毁目前所包含的价值”,如文件所述。当我阅读标准时,在v.emplace(...)之后,变体中的当前值总是被销毁,新类型要么是set类型,要么是无值类型。

我确实知道,条件is_trivially_copyable排除了所有具有可观测析构函数的类型。因此,这也可以是:"as-if变量用旧值重新初始化“左右。但是变体的状态是一个可以观察到的效果。那么,标准是否允许emplace不更改当前值?

根据标准报价编辑:

然后初始化包含的值,就好像直接-非列表-使用参数std​::​forward<Args>(args)...初始化TI类型的值一样。

T tmp {std​::​forward<Args>(args)...}; this->value = std::move(tmp);真的是上述功能的有效实现吗?这就是“好像”的意思吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-11-13 10:45:22

我认为标准的重要部分是:

来自https://timsong-cpp.github.io/cppwp/n4659/variant.mod#12

23.7.3.4 Modifiers (...) 模板variant_alternative_t>&嵌入(Args&&.( args); (...)如果在初始化包含的值期间抛出异常,则变体可能不包含值。

上面写着“可能”而不是“必须”。我希望这是有意的,以便允许像gcc那样的实现。

正如您自己提到的,只有当所有备选方案的析构函数都是微不足道的,因此无法观察到,因为需要销毁以前的值时,这才有可能实现。

后续问题:

代码语言:javascript
复制
Then initializes the contained value as if direct-non-list-initializing a value of type TI with the arguments std​::​forward<Args>(args)....

tmp {std​::​转发(Args).};this->value = std::move(tmp);真的算作上述实现的有效实现吗?这就是“好像”的意思吗?

是的,因为对于属于微不足道的可复制的类型,没有办法检测到差异,因此实现的行为就像初始化了所描述的值一样。如果该类型不具有可复制性,则此操作将不起作用。

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58834389

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档