2017-11-07 24 views
5

My std::variant chứa các loại streamable:Làm thế nào để dòng std :: biến <...,...>

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

Biên soạn với g ++ 7 với -std = C++ 1Z trả về lỗi thời gian biên dịch.

Một đoạn trích:

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

Dường như một std::variant<int, std::string> không có khả năng dòng. Làm thế nào tôi có thể đạt được mà tôi có thể trực tiếp dòng biến thể cho một dòng đầu ra?

mong đợi đầu ra:

1hi 
+5

cái gì đó dọc những dòng này: 'std :: lần ([] (const auto & v) {std :: cout << v;}, a);' –

+1

Boost.Variant hỗ trợ một toán tử chèn, nó không rõ ràng với tôi tại sao điều này bị bỏ qua từ 'std :: variant'. http://www.boost.org/doc/libs/1_65_1/doc/html/boost/operator_idp789915280.html – GManNickG

Trả lời

8

Dòng này cũng lồng nhau biến thể.

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'; 
+0

Có lý do nào giải thích tại sao điều này không có trong tiêu chuẩn không? –

+0

@ClaasBontus câu trả lời thông thường: nó không được đề xuất – sehe

+1

Ai đó có thể làm rõ dòng: mẫu streamer (T) -> streamer ; – LeDYoM

-2

Tôi nghĩ rằng bạn phải sử dụng get chức năng từ std, để có được các loại streamable và không phải là loại biến thể riêng của mình.

Something như thế

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

Trong một tình huống thực tế tôi sẽ sử dụng 'std :: variant' trong một loại container và do đó tôi không biết loại trước. –

+1

Bạn nói đúng, câu trả lời của tôi là sai. Tôi không hiểu ý nghĩa thực sự của câu hỏi. – miraklis

4

Không chắc đó là một ý tưởng tốt nhưng tôi cho rằng bạn có thể xác định một operator<<() cho std::variant.

Just for fun tôi đã nhận ra một trong những bạn có thể thấy trong ví dụ sau (Tôi cho rằng có thể được đơn giản hóa một chút)

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

Một cách khác để viết hàm streamV() helper, mà không có sự T0, Ts... loại nhưng sử dụng 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); } 

- CHỈNH SỬA 2 -

Được chỉ định bởi T.C. (cảm ơn!) Tôi chỉ (với streamV()) đã triển khai phiên bản kém hiệu quả, ít thú vị và ít hữu ích hơn của std::visit().

Sử dụng std::visit() ví dụ của tôi có thể trở thành rất nhiều đơn giản

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

Tôi lặp lại: chỉ để cho vui, bởi vì tôi không nghĩ rằng đó là một ý tưởng tốt xác định operator<<() qua một loại tiêu chuẩn.

Tôi đề xuất giải pháp từ T.C. bao gồm trường hợp biến thể để phát trực tuyến trong một lớp cụ thể.

+3

Chúng ta hãy không thực hiện lại một phiên bản phức tạp tuyến tính của 'std :: visit'. –

+0

@ T.C. - vâng ... Tôi cho rằng tôi đã phát minh lại bánh xe lần khác: ( – max66

Các vấn đề liên quan