访问`std :: variant`的不安全,“noexcept”和无开销方式

时间:2017-02-05 12:35:36

标签: c++ c++17 variant

std::variant提供以下访问功能:

  • std::get_if:将指针带到variant,将指针返回到替代。

    template <std::size_t I, typename... Ts> 
    auto* std::get_if(std::variant<Ts...>* pv) noexcept;
    
    •   

      如果pv不是空指针而是pv->index() == I,则返回指向存储在pv指向的变量中的值的指针。否则,返回空指针值。

      这意味着get_if的实现大致如下:

      template <std::size_t I, typename... Ts> 
      auto* std::get_if(std::variant<Ts...>* pv) noexcept
      {
          if(pv == nullptr) return nullptr;
          if(pv->index() != I) return nullptr;
          return &(pv->real_get<I>());
      }
      
  • std::get:将引用带到variant,将引用返回给替代 throw 无效访问。

    template <std::size_t I, typename... Ts>
    auto& std::get(std::variant<Ts...>& v);
    
    •   

      如果v.index() == I,则返回对v中存储的值的引用。否则,抛出std::bad_variant_access

      这意味着get的实现大致如下:

      template <std::size_t I, typename... Ts> 
      auto& std::get(std::variant<Ts...>& v)
      {
          if(v.index() != I) throw std::bad_variant_access{};
          return v.real_get<I>();
      }
      

我想要一个不安全的访问功能:

  • noexcept

  • 引用带到variant,避免任何pv == nullptr检查。

  • 如果v.index() != I未定义的行为

为什么呢?因为在某些情况下我可以100%确定特定variant实例在代码路径中包含特定类型。此外,在编写已单独检查v.index() != I 的通用代码(例如,编写我自己的visit时,它会很有用。

示例实施:

template <std::size_t I, typename... Ts> 
auto& unsafe_get(std::variant<Ts...>& v)
{
    return v.real_get<I>();
}

标准中是否有这样的内容?我无法找到它。如果没有,是否可以为std::variant实施,或者我是否需要推出自己的variant实施?

2 个答案:

答案 0 :(得分:6)

正如@ T.C所指出的那样。在评论中,你的第一和第三个要求是互不相容的。这在N3279中有详细说明,标题为“保守地使用图书馆中的noexcept”。

基本上有两类合约:窄和宽。函数或操作的合约未指定任何未定义的行为。 这样的合同没有先决条件。只有具有广泛合同的函数在标准库中标记为noexcept

OTOH,狭窄合同是一份不宽的合同。当以违反记录合同的方式调用时,函数或操作的缩小合同会导致未定义行为。它们无法标记为noexcept。相反,你可以期待的最好的是它们被记录为“投掷:没什么。”

您似乎运气不好,并且std::variant的当前提案中未提供此类未经检查的访问权限。

答案 1 :(得分:1)

我认为你必须自己实施整个变体。 虽然不受限制的工会可能会有所帮助 - 它们至少可以解决将多种类型放在同一位置并处理对齐问题。