如何流std :: variant

问题描述 投票:8回答:4

我的std::variant包含流式类型:

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

使用带有-std = c ++ 1z的g ++ 7进行编译会返回编译时间错误。

摘录:

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

似乎std::variant<int, std::string>无法流式传输。如何实现可以直接将变体流式传输到输出流?

预期输出:

1hi
c++ stream c++17 variant
4个回答
10
投票

这也流嵌套的变体。

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

用作:

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

4
投票

不确定这是个好主意,但我想您可以为operator<<()定义一个std::variant

只是为了好玩,我已经意识到您可以在下面的示例中看到这个(我想可以简化一下)

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

-编辑-

编写streamV()辅助函数的另一种方法,不使用T0, Ts...类型,但使用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); }

-编辑2-

如T.C. (谢谢!)我仅(使用streamV())实现了std::visit()的效率较低,不太有趣和使用较少的版本。

使用std::visit()我的示例可能变得简单得多

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

我重复:只是为了好玩,因为我认为在标准类型上定义operator<<()不是一个好主意。

我建议T.C.封装变体实例以在特定类中进行流式传输。


0
投票

注:以下示例摘自Igor Tandetnik对问题本身的评论。


-3
投票

我认为您必须使用std的get函数来获取可流类型,而不是变量类型本身。

© www.soinside.com 2019 - 2024. All rights reserved.