2017-11-07 48 views
5

My std::variant zawiera przesyłania strumieniowego typy:Jak strumień std :: wariant <...,...>

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

kompilacji z g ++ 7 z -std = C++ 1z zwraca błędy podczas kompilacji.

Fragment:

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

pozornie std::variant<int, std::string> nie jest w stanie przesyłać strumieniowo. Jak mogę osiągnąć, że mogę bezpośrednio przesłać wariant do strumienia wyjściowego?

wyjście z oczekiwaniami:

1hi 
+5

coś wzdłuż tych linii: 'std :: wizyty ([] (const auto & v) {std :: cout << v;}, a);' –

+1

Boost.Variant obsługuje operatora wstawiania, nie jest dla mnie jasne, dlaczego zostało to pominięte w 'std :: variant'. http://www.boost.org/doc/libs/1_65_1/doc/html/boost/operator_idp789915280.html – GManNickG

Odpowiedz

8

Ten strumień zawiera również warianty zagnieżdżone.

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

Użyj jako:

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

Czy istnieje powód, dla którego nie jest to zgodne ze standardem? –

+0

@ClaasBontus zwykła odpowiedź: nie została zaproponowana – sehe

+1

Czy ktoś mógłby wyjaśnić linię: szablon streamer (T) -> streamer ; – LeDYoM

-2

myślę, że trzeba użyć funkcji dostać się z dokumentacją techniczną, aby uzyskać przesyłania strumieniowego rodzajów i nie sam typ wariant.

coś w tym

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

W praktycznej sytuacji używałbym 'std :: variant' w typie kontenera, a więc nie znam typu z góry. –

+1

Masz rację, moja odpowiedź jest błędna. Nie zrozumiałem prawdziwego znaczenia pytania. – miraklis

4

nie jestem pewien, że to dobry pomysł, ale przypuszczam, że można zdefiniować operator<<() dla std::variant.

Tak dla zabawy mam sobie sprawę z jednego można zobaczyć w poniższym przykładzie (przypuszczam można uprościć trochę)

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

Innym sposobem Napisać funkcję streamV() pomocnika, bez typów T0, Ts... ale stosując 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); } 

- EDYTUJ 2 -

Jak wskazano przez T.C. (dzięki!) Mam tylko (z streamV()) zaimplementowaną mniej wydajną, mniej interesującą i mniej użyteczną wersję std::visit().

Korzystanie std::visit() moim przykładem może stać się o wiele prostsze

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

Powtarzam: tylko dla zabawy, bo nie sądzę, że to dobry pomysł określić operator<<() ponad standardowego typu.

Proponuję rozwiązanie od T.C. które kopiują warianty instancji do strumienia w określonej klasie.

+3

Nie używajmy ponownie wersji "std :: visit" o złożoności liniowej. –

+0

@ T.C. - tak ... Przypuszczam, że po raz kolejny wymyśliłem koło :( – max66