c++c++17ubuntu-18.04variantgcc7

Why I'm getting std::bad_variant_access with std::variant?


Consider:

#include <variant>
#include <iostream>

int main()
{
    double foo = 666.666;
    std::variant<double, uint64_t> v = foo;
    std::cout << std::get<uint64_t>(v) << std::endl;
    return 0;
}

This results in:

terminate called after throwing an instance of 'std::bad_variant_access'
  what():  Unexpected index
Aborted (core dumped)

Why?

I have understood that std::variant would be a replacement for union. But if this kind of basic stuff fails what is it good for?


Solution

  • I have understood that std::variant would be a replacement for union.

    Yes. Kind of. But your misunderstanding seems to be with unions in C++. From cppreference:

    It's undefined behavior to read from the member of the union that wasn't most recently written. Many compilers implement, as a non-standard language extension, the ability to read inactive members of a union.

    Note that this is different from C, which is one root cause of a common misunderstanding that accessing an inactive member would also be allowed in C++.

    Why?

    Because std::variant mimics unions but add some safety and std::get is...

    Index-based value accessor: If v.index() == I, returns a reference to the value stored in v. Otherwise, throws std::bad_variant_access. The call is ill-formed if I is not a valid index in the variant.

    .

    But if this kind of basic stuff fails what is it good for?

    Unions in C++ were never meant to be used like that. Unions are rather to save memory when you want to squeeze two or more values into the same memory (but at any time only use one of them). I never met a real use case for them. std::variant however, is a nice addition to C++ types. The bigger picture is that already since long time there were so-called product types, like std::pair<T1,T2>, in the sense that the set of values they can represent is T1 x T2. With std::variant (and std::any) now C++ also has proper (= with a degree of typesafety that you never got from unions) sum types, ie the set of values a std::variant<T1,T2> can represent is T1 + T2 (sorry for using sloppy notation, hope it is clear).