How to pass std :: variant <..., ...>

My std::variant contains stream types:

 std::variant<int, std::string> a, b; a = 1; b = "hi"; std::cout << a << b << std::endl; 

Compiling with g ++ 7 with -std = C ++ 1z returns compile-time errors.

Exposure:

 test.cpp: In function 'int main(int, char**)': test.cpp:10:13: error: no match for 'operator<<' (operand types are 'std::ostream {aka std::basic_ostream<char>}' and 'std::variant<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >') std::cout << a << b << std::endl; ~~~~~~~~~~^~~~ 

Apparently std::variant<int, std::string> cannot stream. How can I ensure that I can directly pass this option to the output stream?

Expected Result:

 1hi 
+5
source share
3 answers

This stream is also nested options.

 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'; 
+8
source

Not sure if this is a good idea, but I suppose you could define operator<<() for std::variant .

Just for fun, I realized the one you can see in the following example (I suppose you can simplify it a bit)

 #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 a helper function is streamV() , without the types T0, Ts... , 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 TC pointed out (thanks!) I only (with streamV() ) implemented a less efficient, less interesting and less useful version of std::visit() .

Using std::visit() , my example can be much 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 that a good idea defines operator<<() over a standard type.

I propose a solution from TC that go around the instance option for a thread in a specific class.

+4
source

I think you should use the get function from std to get the stream types, not the type of the variant.

Something like that

 std::cout << std::get<int>(a) << std::get<std::string>(b) << std::endl; 
-2
source

Source: https://habr.com/ru/post/1273193/


All Articles