2017-11-07 4 views
5

Mein std::variant enthält streambare Typen streamen:Wie std :: Variante <...,...>

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

mit g ++ Kompilieren 7 mit -std = C++ 1z gibt Fehler Kompilierung.

Ein Auszug:

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; 
    ~~~~~~~~~~^~~~ 

Scheinbar ein std::variant<int, std::string> ist zu streamen nicht in der Lage. Wie kann ich erreichen, dass ich die Variante direkt in einen Ausgabestream streamen kann?

Erwarteter Ausgang:

1hi 
+5

Etwas in dieser Richtung: 'std :: Besuch ([] (const auto & v) {std :: cout << v;} a);' –

+1

Boost.Variant unterstützt einen Insertion Operator, mir ist nicht klar, warum dies in 'std :: variant' weggelassen wurde. http://www.boost.org/doc/libs/1_65_1/doc/html/boost/operator_idp789915280.html – GManNickG

Antwort

8

Dies streamt auch verschachtelte Varianten.

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; 
} 

Anwendung:

std::cout << streamer{a} << streamer{b} << '\n'; 
+0

Gibt es einen Grund dafür, warum dies nicht im Standard ist? –

+0

@ClaasBontus die übliche Antwort: Es wurde nicht vorgeschlagen – sehe

+1

Könnte jemand die Linie klären: Vorlage Streamer (T) -> Streamer ; – LeDYoM

-2

ich glaube, Sie die get-Funktion aus dem std verwenden müssen, die als Datenstrom Typen zu bekommen und nicht die Variante Typ selbst.

etwas wie die

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

In einer praktischen Situation würde ich 'std :: variant' in einem Containertyp verwenden und daher kenne ich den Typ nicht im Voraus. –

+1

Sie haben Recht, meine Antwort ist falsch. Ich habe die wahre Bedeutung der Frage nicht verstanden. – miraklis

4

nicht sicher, dass es eine gute Idee, aber ich nehme an, Sie ein operator<<() für std::variant definieren könnten.

Just for fun ich die eine realisiert haben Sie im folgenden Beispiel sehen kann (ich nehme an kann ein wenig vereinfacht werden)

#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 -

Eine weitere Möglichkeit, schreiben die streamV() Helferfunktion, ohne die T0, Ts... Typen aber mit 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 -

Wie T.C. (Danke!) Ich habe nur (mit streamV()) eine weniger effiziente, weniger interessante und weniger sinnvolle Version von std::visit() implementiert.

Mit std::visit() meinem Beispiel viel einfacher

#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; 
} 

werden könnte ich wiederhole: nur zum Spaß, weil ich nicht glaube, es ist eine gute Idee operator<<() über einen Standard-Typ definieren.

Ich schlage die Lösung von T.C. die die Varianteninstanz umhüllen, um in einer bestimmten Klasse zu streamen.

+3

Lassen Sie uns die Version von 'std :: visit' mit linearer Komplexität nicht erneut implementieren. –

+0

@ T.C. - Ja ... Ich nehme an, ich habe das Rad ein anderes Mal neu erfunden :( – max66

Verwandte Themen