【问题标题】:How does std::variant becomes valueless_by_exception in this example?在这个例子中,std::variant 如何变成 valueless_by_exception?
【发布时间】:2017-11-24 10:22:49
【问题描述】:

这是受cppreference示例启发的示例

struct S {
    operator int() { throw 42; }
};


int main(){
    variant<float, int> v{12.f}; // OK
    cout << std::boolalpha << v.valueless_by_exception() << "\n";
    try{
        v.emplace<1>(S()); // v may be valueless
    }
    catch(...){
    }
    cout << std::boolalpha << v.valueless_by_exception() << "\n";   
}

对于一个编译器,我尝试过它的输出

假,真

表示emplace 导致变体变得毫无价值

我不明白这是怎么发生的。 特别是我不明白为什么会调用emplace,我希望程序甚至不会调用它,因为从 S 到 int 参数的转换会抛出。

【问题讨论】:

  • 转换发生在emplace函数内部,而不是main

标签: c++ exception c++17 variant


【解决方案1】:

注意相关std::variant::emplace重载的签名:

template <size_t I, class... Args>
std::variant_alternative_t<I, variant>& emplace(Args&&... args);

它需要一组转发引用。这意味着在评估函数参数时不会调用从Sint 的转换运算符;它在emplace 的内部调用。由于尝试在适当的位置构造 int 会失败,因此该变体因异常而变得毫无价值。

也许可以实现variant,这样对于普通可移动类型,在尝试就地构造之前保存旧值,如果失败则恢复,但我不确定它是否适合标准对类型实现的各种限制。

【讨论】:

  • 关于设计决策这里是这个cbeck88.github.io/strict-variant/strict_variant/overview/…的一个很好的链接
  • “我不确定它是否符合标准对类型实现的各种限制。”我听说在 Lib 反射器上引用了这个解释,以解释为什么 emplace 措辞说如果初始化抛出,变体“可能不持有值”,但我很难阅读“如果valueless_by_exception()false,则销毁当前包含的值。"作为“复制当前包含的值,以便稍后在异常时重新插入variant。”
  • Boost 有永不为空的保证……在这种情况下,我相信boost::variant 保存了float 的旧值,因为它可以不被复制/移回。在极少数情况下,无法将任何值恢复为noexcept,它们使用堆分配和“双缓冲”。出于性能原因,valueless_by_exception 被选为std::variant,而不是永不为空的保证,因为boost::variant 的故障模式可能非常令人惊讶。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多