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

Vit*_*meo 11 c++ variant c++17

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

  • std::get_if:取指针variant,返回指针替代.

    template <std::size_t I, typename... Ts> 
    auto* std::get_if(std::variant<Ts...>* pv) noexcept;
    
    Run Code Online (Sandbox Code Playgroud)
    • 如果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>());
      }
      
      Run Code Online (Sandbox Code Playgroud)
  • std::get:取基准variant,回到参考替代,throw对非法访问.

    template <std::size_t I, typename... Ts>
    auto& std::get(std::variant<Ts...>& v);
    
    Run Code Online (Sandbox Code Playgroud)
    • 如果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>();
      }
      
      Run Code Online (Sandbox Code Playgroud)

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

  • 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>();
}
Run Code Online (Sandbox Code Playgroud)

标准中有这样的东西吗?我找不到它.如果没有,是否可以实现std::variant,或者我是否需要推出自己的variant实现?

Tem*_*Rex 6

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

基本上有两类合同:狭义和宽泛.一个广泛的功能或操作合同没有指定任何不确定的行为.这样的合同没有先决条件.noexcept标准库中仅标记具有宽合同的函数.

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

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