Search code examples
c++stdc++17variant

Accessing std::variant using std::get<index>


How can I access members of variant using v.index() then std::get<index>(v)?

Useful when the variant has multiple entries of the same type.

The following does not work. This code doesn't compile on either GCC or clang

#include <iostream>
#include <variant>
#include <string>
#include <sstream>

typedef std::variant<int, int, std::string> foo;

std::string bar(const foo f) {

    const std::size_t fi = f.index();
    auto ff = std::get<fi>(f);

    std::ostringstream ss;      
    ss << "Index:" << fi << "   Value: " << ff;
    return ss.str();
}


int main()
{
    foo f( 0 );

    std::cout << bar(f);
}

There are many versions of std::get of course, so the error messages are lengthy.

gcc complains (for every version of get<>)

prog.cc:10:29: error: the value of 'fi' is not usable in a constant expression
     auto ff = std::get<fi>(f);
                             ^
prog.cc:9:23: note: 'fi' was not initialized with a constant expression
     const std::size_t fi = f.index();
                       ^~
prog.cc:10:29: note: in template argument for type 'long unsigned int'
     auto ff = std::get<fi>(f);

Clang complains (for every version of get<>) (re _Tp or _Ip as the case may be)

candidate template ignored: invalid explicitly-specified argument for template parameter '_Tp' 

Wandbox

UPDATED to ask how to solve rather than what does the error message mean.


Solution

  • std::get<> is applicable when requesting a variant index that is known at compile time.

    If you need to act on a variant value whose type isn't known until runtime, the idiomatic approach is to use a visitor with std::visit.

    #include <iostream>
    #include <variant>
    #include <string>
    
    struct output_visitor
    {
        template< typename T >
        void operator() ( const T& value ) const
        {
            std::cout << value;
        }   
    };
    
    int main()
    {
        std::variant<int, std::string> f( 0 );
    
        std::visit( output_visitor{}, f );
    }
    

    This can often be implemented with C++14 "generic lambdas"

    #include <iostream>
    #include <variant>
    #include <string>
    
    int main()
    {
        std::variant<int, std::string> f( 0 );
    
        std::visit( [](auto v){std::cout << v;} , f );
    }