C++ – Stream/cout for a variant of the form >

I have tried every snippet I can find on streaming the contents of a variant but nothing seems to work in my particular use case:

Given a variant of the form std::variant<int,std::pair<char, char>> myVar how can one go about streaming the contents of myVar without explicitly knowing what myVar contains.

For example:

#include <iostream>
#include <variant>

typedef std::variant<int,std::pair<char, char>> myVar;

template <typename T>
std::ostream& operator<<(std::ostream& os, std::pair<T, T> &p) {
    os << "(" << p.first << ", " << p.second << ")";
    return os;
} // This is so we can stream a pair...

/*
...black box to stream variant here...
*/

std::pair<char, char> testPair = {'X','a'};
int testInt = 5;
myVar testVariant = testInt;

std::cout << testVariant << "n" // Should print 5.

testVariant = testPair;

std::cout << testVariant << "n" // Should print (X, a).

Any snippet I use for streaming variants, for example

template<typename T, typename... Ts>
std::ostream& operator<<(std::ostream& os, const std::variant<T, Ts...>& v)
{
    std::visit([&os](auto&& arg) {
        os << arg;
    }, v);
    return os;
}

It fails in this case and returned the following error message:

"Invalid operands to binary expression ('std::ostream' (aka 'basic_ostream<char>') and 'const std::__1::pair<char, char>')" error — no matter what I try this is all I ever get!

I cannot figure out what is wrong with this code. Please can somebody help me to identify and solve the issue here?

3rd party libraries such as boost are an absolute no-go.

Answer

Your code is (almost) fine, but is not const-correct. A stream output operator should accept the value as a const reference. Yours is not.

While the error messages for template type mismatches can be difficult to read, they do generally tell you the problem. In this case, you’ll see it’s trying to match const types, and the candidate is non-const.

So, the correct fix is to make the second parameter const:

template <typename T>
std::ostream& operator<<(std::ostream& os, const std::pair<T, T> &p) //...

This should be obvious since your operator<< for the actual variant is const. That means it’s impossible to get a non-const reference from anything it contains.

Here is the full working program, with the fix and also some semi-colons that you missed.

#include <iostream>
#include <variant>

typedef std::variant<int,std::pair<char, char>> myVar;

template <typename T>
std::ostream& operator<<(std::ostream& os, const std::pair<T, T> &p)
{
    os << "(" << p.first << ", " << p.second << ")";
    return os;
}

template<typename T, typename... Ts>
std::ostream& operator<<(std::ostream& os, const std::variant<T, Ts...>& v)
{
    std::visit([&os](auto&& arg) { os << arg; }, v);
    return os;
}

int main()
{
    std::pair<char, char> testPair = {'X','a'};
    int testInt = 5;
    myVar testVariant = testInt;
    std::cout << testVariant << "n";
    testVariant = testPair;
    std::cout << testVariant << "n";
}

Output:

5
(X, a)

Leave a Reply

Your email address will not be published. Required fields are marked *