The variant class template is a safe, generic, stack-based discriminated union container, offering a simple solution for manipulating an object from a heterogeneous(异质型) set of types in a uniform manner. Whereas standard containers such as std::vector may be thought of as “multi-value, single type,” variant is “multi-type, single value.

联合(union)的问题

Many times, during the development of a C++ program, the programmer finds himself in need of manipulating several distinct types in a uniform manner. Indeed, C++ features direct language support for such types through its union keyword:

union {int i; int d;} u;
u.d = 3.14;
u.i = 3;  // overwrites u.d 

C++’s union construct, however, is nearly useless in an object-oriented environment. The construct entered the language primarily as a means for preserving compatibility with C, which supports only POD (Plain Old Data) types, and so does not accept types exhibiting non-trivial construction or destruction:

union
{int i;std::string s;        // illegal: std::string is not a POD type!
}u;

关于 POD以及Aggregates类型的详尽讨论请见 C++ Aggregate 与 POD(Plain Old Data)的解释。

基本用法(Basic Usage)

boost::variant<int, std::string> v;

默认地,variant的默认构造函数通过模板中的第一个类型(严格地说,为bounded type,本例为int)进行构造,所以,初始状态下,v 被初始化为int(0)

std::cout << v.type().name() << std::endl;// int
std::cout << v << std::endl;// 0

如果模板中的第一个类型不存在默认构造,将会报错,哪怕后续类型存在默认构造:

class A
{
public:A(int){}
};int main(int, char**)
{boost::variant<A, int> v;// 错误   1   error C2512: “A”: 没有合适的默认构造函数可用return 0;
}

如果这种默认的处理方式不是我们期待的,或者如上例所示,第一个模板类型不存在默认的构造函数,variant对象可直接通过模板中的任意类型的构造函数的方式得以构造;

boost::variant<A, int> v1 = 5;
boost::variant<A, int> v2 = A(5);

类似地,一个variant对象也可通过模板中的任意类型的构造函数进行赋值,

int main(int, char**)
{boost::variant<int, std::string> v;v = "hello";return 0;
}

共有两种方式获取 variant对象中的内容:

  • apply_visitor,安全且强大
  • get<T>,十分方便
std::string& str = boost::get<std::string>(v);
str += " world! ";
std::cout << str << std::endl;// hello world! 

get<T>方法存在一个显著的缺陷,也即,如果一个函数接受variant<int, std::string>,我们无法知道传递进来的variant对象,是int类型还是std::string类型。此时我们便需要进行多一步的判断,

void times_two(boost::variant<int, std::string>& v)
{if (int& a = boost::get<int>(v))a *= 2;if (std::string* s = boost::get<std::string>(&v))s += s;
}

然而这样的代码虽然能够工作,却十分的脆弱,如果不十分小心将会产生一些只在运行期才会出现的逻辑错误。例如,考虑如果我们希望扩展 times_two 操纵一个具有额外类型的variant类型变量。显然,此时我们至少需要修改函数声明:

void times_two(boost::variant<int, std::string, std::complex<double>>& v)
{// 同上...
}

如果我们不对函数体进行任何修改,如果传递进来的是持有std::complex<double>类型的variant对象,函数将不做任何动作,也不会报错。对本例而言,我们很容易知道如何修改,

if (std::complex<double>* c = boost::get<std::complex<double>>(&v))*c *= 2;

在一些更为复杂的程序中,将会花费可观的时间来识别和定位 bug 的位置。

因此,在真实世界应用中,对variant类型对象内容的获取需要一个比get方法更为鲁棒的获取机制,这正是安全且强大的apply_visitor

class times_two_visitor :public boost::static_visitor<>
{
public:void operator()(int& i) const{i *= 2;}void operator()(std::string& s) const{s += s;}
};boost::apply_visitor(times_two_visitor(), v);

为了增强鲁棒性,还可写出如下的 generic 版本:

class times_two_generic :public boost::static_visitor<>
{
public:template<typename T>void operator()(T& operand) const{operand += opearand;}
}int main(int, char**)
{std::vector<boost::variant<int, std::string>> vec;vec.push_back(5);vec.push_back("hello");std::for_each(vec.begin(), vec.end(), boost::apply_visitor(times_two_visitor()));for (auto& elem: vec)std::cout << elem << " ";std::cout << std::endl;return 0;
}

References

[1] Boost.Variant

boost基础——variant的原理及基本用法相关推荐

  1. python装饰器实例-Python装饰器原理与简单用法实例分析

    本文实例讲述了Python装饰器原理与简单用法.分享给大家供大家参考,具体如下: 今天整理装饰器,内嵌的装饰器.让装饰器带参数等多种形式,非常复杂,让人头疼不已.但是突然间发现了装饰器的奥秘,原来如此 ...

  2. 企业实战案例-- LNMP基础架构的原理及部署以及wordpress论坛的安装

    企业实战案例-- LNMP基础架构的原理及部署以及wordpress论坛的安装 LNMP架构原理 一.源码安装mysql 二.源码安装php 三.源码安装nginx 四.安装wordpress论坛 L ...

  3. spark on yarn 完全分布式_Spark编程笔记(1)-架构基础与运行原理

    引言 根据IBM前首席执行官郭士纳的观点,IT领域每隔十五年就会迎来一 次重大变革 .当前我们正处于第三次信息浪潮(2010年前后),物联网.云计算和大数据技术突飞猛进. 信息爆炸是我们当前所需要解决 ...

  4. 搜索引擎工作的基础流程与原理

    搜索引擎最重要的是什么?有人会说是查询结果的准确性,有人会说是查询结果的丰富性,但其实这些都不是搜索引擎最最致命的地方.对于搜索引擎来说,最最致命的是查询时间.试想一下,如果你在百度界面上查询一个关键 ...

  5. python的用途实例-Python基础之函数原理与应用实例详解

    本文实例讲述了Python基础之函数原理与应用.分享给大家供大家参考,具体如下: 目标 函数的快速体验 函数的基本使用 函数的参数 函数的返回值 函数的嵌套调用 在模块中定义函数 01. 函数的快速体 ...

  6. CV:计算机视觉技术之图像基础知识(二)—以python的skimage和numpy库来了解计算机视觉图像基础(图像存储原理-模糊核-锐化核-边缘检测核,进阶卷积神经网络(CNN)的必备基础)

    CV:计算机视觉技术之图像基础知识(二)-以python的skimage和numpy库来了解计算机视觉图像基础(图像存储原理-模糊核-锐化核-边缘检测核,进阶卷积神经网络(CNN)的必备基础) 目录 ...

  7. 【Java基础】HashMap原理详解

    [Java基础]HashMap原理详解 HashMap的实现 1. 数组 2.线性链表 3.红黑树 3.1概述 3.2性质 4.HashMap扩容死锁 5. BATJ一线大厂技术栈 HashMap的实 ...

  8. Java-GUI编程实战之管理系统 Day2【Swing(组件介绍、布局管理器、事件类及监听器类)、基础组件按钮和输入框的用法】

    视频.课件.源码[链接:https://pan.baidu.com/s/13ffqGDzH-DZib6-MFViW3Q 提取码:zjxs] Java-GUI编程实战之管理系统 Day1[项目开发流程. ...

  9. [深度学习基础] 深度学习基础及数学原理

    图像分类 (image classification) 问题是指, 假设给定一系列离散的类别(categories)(如猫, 狗, 飞机, 货车, ...), 对于给定的图像, 从这些类别中赋予一个作 ...

最新文章

  1. 用html5做一条线,使用HTML5 canvas绘制线条的方法
  2. cryptico.js java解密_從Cryptico.js中提取RSA私鑰
  3. object detection错误之no module named nets
  4. 《温故而知新》JAVA基础四
  5. 【转载】socket select模型
  6. [笔记] Ubuntu 18.04安装Docker CE及nvidia-docker2流程
  7. HTTP Content-type整理
  8. 从零开始搭建物联网平台(三)数模设计
  9. Windows系统内置彩蛋
  10. Qt面对高分辨率屏幕的解决方法思考
  11. 中国电信物联网平台入门学习笔记4:连接时间过长,数据传输消失,电信平台显示延迟...
  12. 我在Slager上的LaTeX排版之路|空格命令(连载)
  13. android输入法好用,安卓手机输入法哪个最好用?
  14. windows server 2003忘记密码
  15. ES部分查询方法,elasticsearch查询方法
  16. AWS的十条军规: 过去十年的经验总结
  17. python xpath定位 麦客表单
  18. WWW‘22 推荐系统论文之序列推荐篇
  19. DXF04-02 时间:2021.12.03-2012.12.03 关键词:DXF样条曲线信息
  20. 【Koltin Flow(二)】Flow操作符之末端操作符

热门文章

  1. 剑指offer面试题48. 最长不含重复字符的子字符串(滑动窗口)
  2. win10换源安装opencv-python
  3. mfc 对话框应用程序 如何利用按钮弹出另一对话框
  4. 给程序员的建议(转自李开复)
  5. 【OCR一】字符识别技术总览(转)
  6. java catch匹配忽略_Java 语法 try catch使用容易忽略的细节 BigDecimal
  7. sqlserver没有ldf附加数据库
  8. oracle有三种类型的异常错误: 预定义 ( Predefined )错误里面的常见错误
  9. Linux系统磁盘阵列创建教程----------(better late than never. 只要开始,虽晚不迟。)...
  10. springboot整合curator实现分布式锁