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

[亡魂溺海] 提交于 2019-12-04 17:50:03

问题


std::variant provides the following access functions:

  • std::get_if: take pointer to variant, return pointer to alternative.

    template <std::size_t I, typename... Ts> 
    auto* std::get_if(std::variant<Ts...>* pv) noexcept;
    
    • If pv is not a null pointer and pv->index() == I, returns a pointer to the value stored in the variant pointed to by pv. Otherwise, returns a null pointer value.

      This means that get_if's implementation roughly looks like this:

      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: take reference to variant, return reference to alternative, throw on invalid access.

    template <std::size_t I, typename... Ts>
    auto& std::get(std::variant<Ts...>& v);
    
    • If v.index() == I, returns a reference to the value stored in v. Otherwise, throws std::bad_variant_access.

      This means that get's implementation roughly looks like this:

      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>();
      }
      

I want an unsafe access function that:

  • Is noexcept.

  • Takes a reference to a variant, avoiding any pv == nullptr check.

  • Has undefined behavior if v.index() != I.

Why? Because there may be some situations where I am 100% sure that a particular variant instance contains a specific type in a code path. Also, it would be useful when writing generic code that already separately checked v.index() != I (e.g. writing my own visit).

Example implementation:

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

Is there something like this in the standard? I couldn't find it. If not, is this possible to implement for std::variant, or do I need to roll out my own variant implementation?


回答1:


As pointed out by @T.C. in the comments, your first and third desiderata are mutually incompatible. This was spelled out in N3279, titled "Conservative use of noexcept in the Library".

There are essentially two classes of contracts: narrow and wide. A wide contract for a function or operation does not specify any undefined behavior. Such a contract has no preconditions. Only functions with wide contracts are marked noexcept in the Standard Library.

OTOH, a narrow contract is a contract which is not wide. Narrow contracts for a functions or operations result in undefined behavior when called in a manner that violates the documented contract. They cannot be marked noexcept. Instead, the best you can expect is that they are documented as "Throws: Nothing."

It appears that you are out of luck and no such unchecked access is provided in the current proposals for std::variant.




回答2:


I think you have to implement the whole variant on your own. Though unrestricted unions can be helpful - they at least solve putting multiple types in the same location with alignment issue handled.



来源:https://stackoverflow.com/questions/42052122/unsafe-noexcept-and-no-overhead-way-of-accessing-stdvariant

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!