【问题标题】:How to stream std::variant<...,...>如何流式传输 std::variant<...,...>
【发布时间】:2018-04-20 11:27:21
【问题描述】:

我的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&lt;int, std::string&gt; 无法流式传输。我怎样才能实现我可以直接将变体流式传输到输出流?

预期输出:

1hi

【问题讨论】:

  • 类似这样的事情:std::visit([](const auto&amp; v) { std::cout &lt;&lt; v; }, a);
  • Boost.Variant 支持插入运算符,我不清楚为什么 std::variant 中省略了它。 boost.org/doc/libs/1_65_1/doc/html/boost/…

标签: c++ stream c++17 variant


【解决方案1】:

这也流式传输嵌套变体。

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

【讨论】:

  • 有什么理由不符合标准吗?
  • @ClaasBontus 通常的答案:没有提议
  • 有人可以澄清一下:template streamer(T) -> streamer;
  • @LeDYoM 允许对流媒体类进行模板参数推导,所以你不需要写streamer&lt;lots-of-stuff&gt;{a}。查看this article中的用户定义的扣除指南部分
  • 请注意,在 C++20 中不需要推导指南,它为聚合类模板隐式生成它们。
【解决方案2】:

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

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

#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&lt;&lt;() 不是一个好主意。

我建议 T.C. 的解决方案。将变体实例封装在特定类中流式传输。

【讨论】:

  • 我们不要重新实现 std::visit 的线性复杂度版本。
  • @T.C. - 是的......我想我又重新发明了轮子:(
【解决方案3】:

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

std::visit 是标准库中的一个函数,可用于此目的:

#include <variant>
#include <iostream>

int main() {
    std::variant<int, std::string> value = 42;

    std::visit([](const auto &elem) { std::cout << elem << '\n'; }, value);
}

上面的sn-p本质上是一种花哨的写法:

#include <variant>
#include <iostream>

int main() {
    std::variant<int, std::string> value = 42;

    if(std::holds_alternative<int>(value)) {
      std::cout << std::get<int>(value) << '\n';
    } else {
      std::cout << std::get<std::string>(value) << '\n';
    }
}

【讨论】:

  • 不明白为什么人们没有为这个答案投票。
猜你喜欢
  • 1970-01-01
  • 2011-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-10
  • 2019-10-11
  • 2021-07-16
相关资源
最近更新 更多