How to stream std::variant<...,...>
This streams nested variants too.
template<class T>
struct streamer {
const T& val;
};
template<class T> streamer(T) -> streamer<T>;
template<class T>
std::ostream& operator<<(std::ostream& os, streamer<T> s) {
os << s.val;
return os;
}
template<class... Ts>
std::ostream& operator<<(std::ostream& os, streamer<std::variant<Ts...>> sv) {
std::visit([&os](const auto& v) { os << streamer{v}; }, sv.val);
return os;
}
Use as:
std::cout << streamer{a} << streamer{b} << '\n';
Not sure it's a good idea but I suppose you could define an operator<<()
for std::variant
.
Just for fun I've realized the one you can see in the following example (I suppose can be simplified a little)
#include <variant>
#include <iostream>
template <std::size_t I, typename T0, typename ... Ts>
std::enable_if_t<(I == 1U+sizeof...(Ts)), std::ostream &>
streamV (std::ostream & s, std::variant<T0, Ts...> const &)
{ return s; }
template <std::size_t I, typename T0, typename ... Ts>
std::enable_if_t<(I < 1U+sizeof...(Ts)), std::ostream &>
streamV (std::ostream & s, std::variant<T0, Ts...> const & v)
{ return I == v.index() ? s << std::get<I>(v) : streamV<I+1U>(s, v); }
template <typename T0, typename ... Ts>
std::ostream & operator<< (std::ostream & s,
std::variant<T0, Ts...> const & v)
{ return streamV<0U>(s, v); }
int main ()
{
std::variant<int, std::string> a, b;
a = 1;
b = "hi";
std::cout << a << b << std::endl;
}
-- EDIT --
Another way to write the streamV()
helper function, without the T0, Ts...
types but using std::variant_size_v
template <std::size_t I, typename V>
std::enable_if_t<(I == std::variant_size_v<V>), std::ostream &>
streamV (std::ostream & s, V const &)
{ return s; }
template <std::size_t I, typename V>
std::enable_if_t<(I < std::variant_size_v<V>), std::ostream &>
streamV (std::ostream & s, V const & v)
{ return I == v.index() ? s << std::get<I>(v) : streamV<I+1U>(s, v); }
-- EDIT 2 --
As pointed by T.C. (thanks!) I've only (with streamV()
) implemented a less efficient, less interesting and less useful version of std::visit()
.
Using std::visit()
my example could become a lot simpler
#include <variant>
#include <iostream>
template <typename T0, typename ... Ts>
std::ostream & operator<< (std::ostream & s,
std::variant<T0, Ts...> const & v)
{ std::visit([&](auto && arg){ s << arg;}, v); return s; }
int main ()
{
std::variant<int, std::string> a, b;
a = 1;
b = "hi";
std::cout << a << b << std::endl;
}
I repeat: just for fun, because I don't think it's a good idea define operator<<()
over a standard type.
I suggest the solution from T.C. that envelope the variant instance to stream in a specific class.
Note: The following example was extracted from a comment from Igor Tandetnik on the question itself.
std::visit
is a function in the standard library which can be used for this exact purpose:
#include <variant>
#include <iostream>
int main() {
std::variant<int, std::string> value = 42;
std::visit([](const auto &elem) { std::cout << elem << '\n'; }, value);
}
The snippet above is essentially a fancy way of writing:
#include <variant>
#include <iostream>
int main() {
std::variant<int, std::string> value = 42;
if(std::holds_alternative<int>(value)) {
std::cout << std::get<int>(value) << '\n';
} else {
std::cout << std::get<std::string>(value) << '\n';
}
}