Search code examples
c++c++17variant

Why can I not retrieve the index of a variant and use that to get its content?


I'm trying to access the content of a variant. I don't know what's in there, but thankfully, the variant does. So I thought I'll just ask the variant what index it is on and then use that index to std::get its content.

But this does not compile:

#include <variant>

int main()
{
  std::variant<int, float, char> var { 42.0F };

  const std::size_t idx = var.index();

  auto res = std::get<idx>(var);

  return 0;
}

The error happens in the std::get call:

error: no matching function for call to ‘get<idx>(std::variant<int, float, char>&)’
   auto res = std::get<idx>(var);
                               ^
In file included from /usr/include/c++/8/variant:37,
                 from main.cpp:1:
/usr/include/c++/8/utility:216:5: note: candidate: ‘template<long unsigned int _Int, class _Tp1, class _Tp2> constexpr typename std::tuple_element<_Int, std::pair<_Tp1, _Tp2> >::type& std::get(std::pair<_Tp1, _Tp2>&)’
     get(std::pair<_Tp1, _Tp2>& __in) noexcept
     ^~~
/usr/include/c++/8/utility:216:5: note:   template argument deduction/substitution failed:
main.cpp:9:31: error: the value of ‘idx’ is not usable in a constant expression
   auto res = std::get<idx>(var);
                               ^
main.cpp:7:15: note: ‘std::size_t idx’ is not const
   std::size_t idx = var.index();
               ^~~

How can I fix this?


Solution

  • Essentially, you cannot.

    You wrote:

    I don't know what's in there, but thankfully, the variant does

    ... but only at run-time, not at compile-time.
    And that means your idx value is not compile-time.
    And that means you can't use get<idx>() directly.

    Something you could do is have a switch statement; ugly, but it would work:

    switch(idx) {
    case 0: { /* code which knows at compile time that idx is 0 */ } break;
    case 1: { /* code which knows at compile time that idx is 1 */ } break;
    // etc. etc.
    }
    

    This is rather ugly however. As comments suggest, you might as well std::visit() (which is not very different from the code above, except using variadic template arguments instead of being this explicit) and avoid the switch altogether. For other index-based approaches (not specific to std::variant), see:

    Idiom for simulating run-time numeric template parameters?