首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何流std::variant<.,...>

如何流std::variant<.,...>
EN

Stack Overflow用户
提问于 2017-11-07 22:29:18
回答 3查看 4.5K关注 0票数 13

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

代码语言:javascript
复制
std::variant<int, std::string> a, b;
a = 1;
b = "hi";
std::cout << a << b << std::endl;

使用g++7进行-std=c++1z编译将返回编译时错误。

节选:

代码语言:javascript
复制
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>无法流。如何实现将变体直接流到输出流?

预期产出:

代码语言:javascript
复制
1hi
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2017-11-07 23:28:39

这也会流嵌套变体。

代码语言:javascript
复制
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;
}

用作:

代码语言:javascript
复制
std::cout << streamer{a} << streamer{b} << '\n';
票数 17
EN

Stack Overflow用户

发布于 2017-11-07 23:02:27

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

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

代码语言:javascript
复制
#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

代码语言:javascript
复制
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(),我的示例可以变得更简单

代码语言:javascript
复制
#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.的解决方案,该解决方案将变量实例封装为特定类中的流。

票数 6
EN

Stack Overflow用户

发布于 2019-12-25 11:26:04

注意:下面的示例是从Igor Tandetnik关于问题本身的评论中提取的。

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

代码语言:javascript
复制
#include <variant>
#include <iostream>

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

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

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

代码语言:javascript
复制
#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';
    }
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47168477

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档