4

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.

18
  • The template parameter to std::get has to be either the index or the type of the element in the variant. So your choices are <0>, <1>, <int>, or <std::string>. Commented Aug 1, 2018 at 1:31
  • Actually the standard specifies std::size_t Commented Aug 1, 2018 at 1:34
  • @md5i non-type template arguments can be a different type to the parameter, so long as there is a unique implicit conversion available (roughly speaking) Commented Aug 1, 2018 at 1:35
  • Is your question actually about how to output the currently active member of a variant? (not necessarily using get) Commented Aug 1, 2018 at 1:36
  • @M.M Yes I guess so. Specifically when there are multiple uses of a type in the variant. Commented Aug 1, 2018 at 1:38

2 Answers 2

13

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 );
}
Sign up to request clarification or add additional context in comments.

7 Comments

note: OP specifically asks about the case of a repeated type in the variant type list; in which case you couldn't use the initializer ( 0 )
There are further examples on cppreference visit page
Thanks! Also, I'd like to know the value of index() - not to use in a get<> though. I guess you could pass f in through output_visitor constructor.
This is the answer? And how are you going to differentiate first int from the second in this visitor?
@Slava If that distinction was important (It's not if the goal is just to output the value) then you could also read f.index() at any stage
|
3

gcc 8.1's error output also includes the explanation:

<source>:10:29: error: the value of 'fi' is not usable in a constant expression
    auto ff = std::get<fi>(f);
                        ^
<source>:9:23: note: 'fi' was not initialized with a constant expression
   const std::size_t fi = f.index();

Integer template arguments have to be constant expressions. f is not a constant expression, therefore a call to its non-static member function is not a constant expression, therefore fi is not.

You can get a better error message with:

constexpr std::size_t fi = f.index();

The code get<fi>(f) could only work if f were also declared to be constexpr ; but that is only possible if all the types in the variant have trivial destructors, which std::string does not.

6 Comments

Thanks! I updated with a better question. How do I solve the problem rather than what does the error message mean. Also a more complicated example.
@KarlM you just can't do get<fi> , types must be known at compile-time
I don't understand your comment. Of course the types are known at compile-time.
@KarlM It is not known when compiling std::string bar(const foo f) whether the active member of f is int or string; so you cannot have an expression referring to the active member of f because that expression's type would not be known at compile-time
ok , hence constexpr. I understand. So what do I do about it?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.