如何流式传输std :: variant <...,...>

Tom*_*Tom 7 c++ stream variant c++17

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

std::variant<int, std::string> a, b;
a = 1;
b = "hi";
std::cout << a << b << std::endl;
Run Code Online (Sandbox Code Playgroud)

使用带有-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;
   ~~~~~~~~~~^~~~
Run Code Online (Sandbox Code Playgroud)

貌似std::variant<int, std::string>是不能流.我怎样才能实现我可以直接将变量流式传输到输出流?

预期产量:

1hi
Run Code Online (Sandbox Code Playgroud)

T.C*_*.C. 9

这也会嵌套变体.

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;
}
Run Code Online (Sandbox Code Playgroud)

用于:

std::cout << streamer{a} << streamer{b} << '\n';
Run Code Online (Sandbox Code Playgroud)

  • @LeDYoM 它允许对流光类进行模板参数推导,因此您无需编写 `streamer&lt;lots-of-stuff&gt;{a}`。从[本文](http://en.cppreference.com/w/cpp/language/class_template_argument_deduction)查看**用户定义的推导指南**部分 (3认同)
  • 有什么原因导致这不在标准中吗? (2认同)
  • 有人可以澄清这一行: template&lt;class T&gt; streamer(T) -&gt; streamer&lt;T&gt;; (2认同)

max*_*x66 6

不确定这是个好主意,但我想你可以定义一个operator<<()for 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;
}
Run Code Online (Sandbox Code Playgroud)

- 编辑 -

编写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); }
Run Code Online (Sandbox Code Playgroud)

-- 编辑 2 --

正如 TC 所指出的(谢谢!)我只(使用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;
}
Run Code Online (Sandbox Code Playgroud)

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

我建议来自 TC 的解决方案将变体实例封装到特定类中。

  • 让我们不要重新实现 `std::visit` 的线性复杂版本。 (4认同)

asy*_*nts 5

注意:以下示例摘自 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);
}
Run Code Online (Sandbox Code Playgroud)

上面的代码片段本质上是一种奇特的写作方式:

#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';
    }
}
Run Code Online (Sandbox Code Playgroud)