Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c++

c++17

variant

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?

like image 452
Vittorio Romeo Avatar asked Feb 05 '17 12:02

Vittorio Romeo


1 Answers

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.

like image 181
Alex Guteniev Avatar answered Nov 15 '22 16:11

Alex Guteniev