访问"std::variant"的不安全、"noexcept"和无开销方式

Unsafe, `noexcept` and no-overhead way of accessing `std::variant`

本文关键字:quot 方式 noexcept 开销 std variant 访问 不安全      更新时间:2023-10-16

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

  • std::get_if:将指针带到variant,将pointer返回到备选方案。

    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实现

正如@T.C.在评论中指出的,您的第一个和第三个需求是相互不兼容的。这在N3279中有详细说明,标题为"图书馆中保守使用noexcept"。

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

OTOH,合同是不宽的合同。当以违反文档约定的方式调用时,函数或操作的狭义约定会导致未定义的行为。它们不能标记为noexcept。相反,你能期待的最好的结果是,它们被记录为"投掷:什么都没有">

看来您运气不好,std::variant的当前提案中没有提供这种未经检查的访问。

我认为您必须自己实现整个变体。尽管不受限制的联合可能会有所帮助,但它们至少可以解决将多个类型放在同一位置并处理对齐问题的问题。