boost基础——variant的原理及基本用法
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的原理及基本用法相关推荐
- python装饰器实例-Python装饰器原理与简单用法实例分析
本文实例讲述了Python装饰器原理与简单用法.分享给大家供大家参考,具体如下: 今天整理装饰器,内嵌的装饰器.让装饰器带参数等多种形式,非常复杂,让人头疼不已.但是突然间发现了装饰器的奥秘,原来如此 ...
- 企业实战案例-- LNMP基础架构的原理及部署以及wordpress论坛的安装
企业实战案例-- LNMP基础架构的原理及部署以及wordpress论坛的安装 LNMP架构原理 一.源码安装mysql 二.源码安装php 三.源码安装nginx 四.安装wordpress论坛 L ...
- spark on yarn 完全分布式_Spark编程笔记(1)-架构基础与运行原理
引言 根据IBM前首席执行官郭士纳的观点,IT领域每隔十五年就会迎来一 次重大变革 .当前我们正处于第三次信息浪潮(2010年前后),物联网.云计算和大数据技术突飞猛进. 信息爆炸是我们当前所需要解决 ...
- 搜索引擎工作的基础流程与原理
搜索引擎最重要的是什么?有人会说是查询结果的准确性,有人会说是查询结果的丰富性,但其实这些都不是搜索引擎最最致命的地方.对于搜索引擎来说,最最致命的是查询时间.试想一下,如果你在百度界面上查询一个关键 ...
- python的用途实例-Python基础之函数原理与应用实例详解
本文实例讲述了Python基础之函数原理与应用.分享给大家供大家参考,具体如下: 目标 函数的快速体验 函数的基本使用 函数的参数 函数的返回值 函数的嵌套调用 在模块中定义函数 01. 函数的快速体 ...
- CV:计算机视觉技术之图像基础知识(二)—以python的skimage和numpy库来了解计算机视觉图像基础(图像存储原理-模糊核-锐化核-边缘检测核,进阶卷积神经网络(CNN)的必备基础)
CV:计算机视觉技术之图像基础知识(二)-以python的skimage和numpy库来了解计算机视觉图像基础(图像存储原理-模糊核-锐化核-边缘检测核,进阶卷积神经网络(CNN)的必备基础) 目录 ...
- 【Java基础】HashMap原理详解
[Java基础]HashMap原理详解 HashMap的实现 1. 数组 2.线性链表 3.红黑树 3.1概述 3.2性质 4.HashMap扩容死锁 5. BATJ一线大厂技术栈 HashMap的实 ...
- Java-GUI编程实战之管理系统 Day2【Swing(组件介绍、布局管理器、事件类及监听器类)、基础组件按钮和输入框的用法】
视频.课件.源码[链接:https://pan.baidu.com/s/13ffqGDzH-DZib6-MFViW3Q 提取码:zjxs] Java-GUI编程实战之管理系统 Day1[项目开发流程. ...
- [深度学习基础] 深度学习基础及数学原理
图像分类 (image classification) 问题是指, 假设给定一系列离散的类别(categories)(如猫, 狗, 飞机, 货车, ...), 对于给定的图像, 从这些类别中赋予一个作 ...
最新文章
- 用html5做一条线,使用HTML5 canvas绘制线条的方法
- cryptico.js java解密_從Cryptico.js中提取RSA私鑰
- object detection错误之no module named nets
- 《温故而知新》JAVA基础四
- 【转载】socket select模型
- [笔记] Ubuntu 18.04安装Docker CE及nvidia-docker2流程
- HTTP Content-type整理
- 从零开始搭建物联网平台(三)数模设计
- Windows系统内置彩蛋
- Qt面对高分辨率屏幕的解决方法思考
- 中国电信物联网平台入门学习笔记4:连接时间过长,数据传输消失,电信平台显示延迟...
- 我在Slager上的LaTeX排版之路|空格命令(连载)
- android输入法好用,安卓手机输入法哪个最好用?
- windows server 2003忘记密码
- ES部分查询方法,elasticsearch查询方法
- AWS的十条军规: 过去十年的经验总结
- python xpath定位 麦客表单
- WWW‘22 推荐系统论文之序列推荐篇
- DXF04-02 时间:2021.12.03-2012.12.03 关键词:DXF样条曲线信息
- 【Koltin Flow(二)】Flow操作符之末端操作符
热门文章
- 剑指offer面试题48. 最长不含重复字符的子字符串(滑动窗口)
- win10换源安装opencv-python
- mfc 对话框应用程序 如何利用按钮弹出另一对话框
- 给程序员的建议(转自李开复)
- 【OCR一】字符识别技术总览(转)
- java catch匹配忽略_Java 语法 try catch使用容易忽略的细节 BigDecimal
- sqlserver没有ldf附加数据库
- oracle有三种类型的异常错误: 预定义 ( Predefined )错误里面的常见错误
- Linux系统磁盘阵列创建教程----------(better late than never. 只要开始,虽晚不迟。)...
- springboot整合curator实现分布式锁