Boost开发指南-4.3optional

news2024/11/26 11:48:16

optional

在实际的软件开发过程中我们经常会遇到“无效值”的情况,例如函数并不是总能返回有效值,很多时候函数正确执行了,但结果却不是合理的值。如果用数学语言来解释,就是返回值位于函数解空间之外。

求一个数的倒数,在实数域内开平方,在字符串中查找子串,它们都可能返回“无效值”。有些无效返回的情况可以用抛出异常的方式来通知用户,但有的情况下这样代价很高或者不允许异常,这时必须要以某种合理的、高效的方式通知用户。

表示“无效值”最常用的做法是增加一个“哨兵”的角色,它位于解空间之外,如 NULL、-1、EOF、string::npos、vector::end()等。但这些做法不够通用,而且很多时候不存在解空间之外的“哨兵”。另外一个方法是使用pair<T, bool>的方式,用一个额外的bool值来标记值是否有效,标准容器set 的insert函数就是如此。

optional使用“容器”语义,包装了“可能产生无效值”的对象,实现了“未初始化”的概念,为这种“无效值”的情形提供了一个更好的解决方案。它已经被提议加入C++17标准。

optional位于名字空间boost,为了使用optional ,需要包含头文件<boost/optional.hpp>,即:

#include <boost/optional.hpp>
using namespace boost;

类摘要

optional库首先定义了常量boost::none,明确了“无效值”的含义:

namespace detail { struct none_helper(); }
typedef int detail::none_helper::*none_t; //定义类型none_t
none_t const none = (static_cast<none_t>(0)); //定义常量none

boost::none有些类似C++11标准里的空指针nullptr,表示未初始化,它的none_t类型实际上是一个成员变量指针,“指向”并不存在的detail::none_helper的int成员。

optional库的核心类是optional,它很像是一个仅能存放一个元素的容器,实现了“未初始化”的概念:如果元素未初始化,那么容器就是空的,否则,容器内就是有效的、已经初始化的值。

optional的类摘要如下:

template<class T>
class optional
{
public:
   optional(); //构造函数
   optional(none_t);
   optional(T const& v);
   optional(bool condition, T v);
   optional& operator= (T const& rhs); //赋值操作符
   template<class... Args>
   void emplace(Args...&& args); //就地创建
   T* operator->(); //重载操作符
   T& operator*();
   T& get(); //访问值
   T* get_ptr();
   T& value(); //访问值,可能抛出异常
   T const& value_or(T const& default) const ;
   template <typename F>
   T value_or_eval(F f) const;
   explicit operator bool() const; //显式bool转型
   bool operator!() const; // bool测试
};

optional的真实接口很复杂,因为它要能够包装任何的类型,但实际的接口还是比较简单并且易于理解的,接下来将进行详细说明。

操作函数

optional 的模板类型参数r可以是任何类型,就如同一个标准容器对元素的要求,并不需要T具有缺省构造函数,但必须是可拷贝构造的,因为optional需要在内部拷贝值。

可以有很多方式创建optional对象,例如:
1)无参的optional()或者optional(boost::none)构造一个未初始化optional对象;
2)optional(v)构造一个已初始化的optional对象,内部拷贝v的值。如果模板类型为T&,那么optional内部持有对引用的包装;
3)optional(condition, v)根据条件condition来构造 optional对象,如果条件成立(true)则初始化为v,否则为未初始化;
4)optional支持拷贝构造和赋值操作,可以从另一个optional对象构造;
5)emplace()是一个特殊的“赋值”函数,可以使用参数就地创建对象,避免了构造后再拷贝的代价。
6)想让一个optional对象重新恢复到未初始化状态可以向对象赋none值。

optional采用了指针语义来访问内部保存的元素,这使得optional未初始化时的行为就像一个空指针,可以使用explicit operator bool()和 operator!()来检测optional是否有效。

optional也重载了operator*和 operator->以实现与指针相同的操作,get()和get_ptr()能够以函数的形式获得元素的引用和指针。注意:它们内部仅使用BOOST_ASSERT提供基本的安全保证,如果 optional未初始化,那么函数的行为是未定义的。

optional另外提供三个value()系列成员函数,它们比 operator*和 operator->更加安全:
1)value()同样可以访问元素,但如果optional未初始化会抛出bad_optional_access 异常;
2)value_or(default)可以保证返回一个有效的值,如果 optional已初始化,那么返回内部的元素,否则返回default;
3)value_or_eval(f)类似value_or(),但它的参数是一个可调用的函数或者函数对象,如果 optional未初始化则返回f的执行结果即f()。

optional还全面支持比较运算,与普通指针比较的“浅比较”(仅比较指针值)不同,optional的比较是“深比较”,同时加入了对未初始化情况的判断。

用法

optional 的接口简单明了,把它认为是一个大小为1并且行为类似指针的容器就可以了,或者把它想象成是一个类似 scoped_ptr、shared_ptr 的智能指针(但要小心,optional不是智能指针,用法类似但用途不同)。

示范optional基本用法的代码如下:

#include <boost/optional.hpp>
using namespace boost;
int main()
{
	optional<int> op0; //一个未初始化的optional对象
	optional<int> op1(none); //同上,使用none赋予未初始化值

	assert(!op0); //bool测试
	assert(op0 == op1); //比较两个optional对象
	assert(op1.value_or(253) == 253); //获取缺省值

	cout << op1.value_or_eval( //使用函数对象
		[]() {return 874; }) << endl; //lambda表达式定义函数对象

	optional<string> ops("test"); //初始化为字符串test
	cout << *ops << endl; //用解引用操作符获取值

	ops.emplace("monado", 3); //就地创建一个字符串,没有拷贝代价
	assert(*ops == "mon"); //只使用了前三个字符

	vector<int> v(10);
	optional<vector<int>& > opv(v); //容纳一个容器的引用
	assert(opv); //bool转型

	opv->push_back(5); //使用箭头操作符操纵容器
	assert(opv->size() == 11);

	opv = none; //置为未初始化状态
	assert(!opv); //此时为无效值
}

这段代码演示了optional 的一些基本操作,接下来我们再看一个略微复杂的例子,代码使用optional作为函数的返回值,解决了本节一开始提出的几个问题:

optional<double> calc(int x) //计算倒数
{
	return optional<double>(x != 0, 1.0 / x); //条件构造函数
}

optional<double> sqrt_op(double x) //计算实数的平方根
{
	return optional<double>(x > 0, sqrt(x)); //条件构造函数
}

int main()
{
	optional<double> d = calc(10);

	if (d) //bool语境测试optional的有效性
	{
		cout << *d << endl;
		cout << d.value() << endl;
	}

	d = sqrt_op(-10);
	if (!d) //使用重载的逻辑非操作符
	{
		cout << "no result" << endl;
	}
}

工厂函数

optional提供一个类似 make_pair()、 make_shared()的工厂函数make_optional(),可以根据参数类型自动推导 optional的类型,用来辅助创建optional对象。它的声明是:

optional<T> make_optional(T const& v);
optional<T> make_optional(bool condition, T const& v);

但make_optional()无法推导出T引用类型的 optional对象,如果需要一个optional<T&>的对象就不能使用make_optional()函数。

make_optional()也不支持emplace的用法,可能存在值的拷贝代价。

int main()
{
	auto x = make_optional(5); //使用auto关键字自动推导类型
	assert(*x == 5);

	auto y = make_optional<double>((*x > 10), 1.0); //模板参数明确类型
	assert(!y);
}

代码示例

#include <cmath>
#include <type_traits>
#include <iostream>
using namespace std;

#define BOOST_DISABLE_ASSERTS
#include <boost/optional.hpp>
using namespace boost;

//
void case1()
{
	cout << typeid(none).name() << endl;
	cout << std::is_member_object_pointer<none_t>::value << endl;
}

//
void case2()
{
	optional<int> op0;
	optional<int> op1(none);

	assert(!op0);
	assert(op0 == op1);
	assert(op1.value_or(253) == 253);

	cout << op1.value_or_eval(
		[]() {return 874; }) << endl;

	optional<string> ops("test");
	cout << *ops << endl;

	ops.emplace("monado", 3);
	assert(*ops == "mon");

	vector<int> v(10);
	optional<vector<int>& > opv(v);
	assert(opv);

	opv->push_back(5);
	assert(opv->size() == 11);

	opv = none;
	assert(!opv);
}

//
optional<double> calc(int x)
{
	return optional<double>(x != 0, 1.0 / x);
}

optional<double> sqrt_op(double x)
{
	return optional<double>(x > 0, sqrt(x));
}

void case3()
{
	optional<double> d = calc(10);

	if (d)
	{
		cout << *d << endl;
		cout << d.value() << endl;
	}

	d = sqrt_op(-10);
	if (!d)
	{
		cout << "no result" << endl;
	}
}

//
void case4()
{
	auto x = make_optional(5);
	assert(*x == 5);

	auto y = make_optional<double>((*x > 10), 1.0);
	assert(!y);
}


//
int main()
{
	case1();
	case2();
	case3();
	case4();
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/859017.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Twitter霸屏:掌握社交流量密码

Twitter群推王发现&#xff0c;Twitter以其简短而有力的信息传递方式而著名&#xff0c;其中字符限制仅有280个。这意味着在Twitter的世界中&#xff0c;迅速高效的沟通至关重要。拥有约321亿月活跃用户的Twitter&#xff0c;成为塑造资源品牌知名度的强大平台。如今&#xff0…

PHP智能人才招聘网站mysql数据库web结构apache计算机软件工程网页wamp

一、源码特点 PHP智能人才招聘网站 是一套完善的web设计系统&#xff0c;对理解php编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。 下载地址 https://download.csdn.net/download/qq_41221322/88199392 视频演示 PH…

Alpine Ridge控制器使其具备多种使用模式 - 英特尔发布雷电3接口:竟和USB Type-C统一了

同时又因为这建立在Type-C的基础上&#xff0c;雷电3也将利用现有的标准Type-C线缆引入有源支持。当使用Type-C的线缆时&#xff0c;雷电的速度就降到了20Gbps全双工——这与普通的Type-C的带宽相同——这是为了成本牺牲了一些带宽。可以比较一下&#xff0c;Type-C线的成本只有…

基于Kubeadm部署k8s集群:下篇

继续上篇内容 目录 7、安装flannel 8、节点管理命令 三、安装Dashboard UI 1、部署Dashboard 2、开放端口设置 3、权限配置 7、安装flannel Master 节点NotReady 的原因就是因为没有使用任何的网络插件&#xff0c;此时Node 和Master的连接还不正常。目前最流行的Kuber…

【内网穿透】实现无公网IP远程连接Linux服务器并安装部署MongoDB数据库

【内网穿透】实现无公网IP远程连接Linux服务器并安装部署MongoDB数据库 ​ 憧憬Blog主页 在强者的眼中&#xff0c;没有最好&#xff0c;只有更好。 全栈开发领域优质创作者&#xff0c;阿里云专家博主 文章目录 【内网穿透】实现无公网IP远程连接Linux服务器并安装部署MongoDB…

智慧工地源码,Spring Cloud+ Vue+UniApp开发,微服务架构

智慧工地源码&#xff0c;智慧工地云平台源码 智慧工地APP源码 智慧工地的核心是数字化&#xff0c;它通过传感器、监控设备、智能终端等技术手段&#xff0c;实现对工地各个环节的实时数据采集和传输&#xff0c;如环境温度、湿度、噪音等数据信息&#xff0c;将数据汇集到云…

扎实推动产学研深入合作,携手共谋高质量发展新篇

近日&#xff0c;华南理工大学自动化学院胡跃明教授等一行4人莅临科东软件&#xff0c;双方就“产学研融合”进行会谈交流。在科东软件总经理余世清等人的陪同下&#xff0c;华工胡教授一行参观了科东软件展厅&#xff0c;对科东软件自主研发的Intewell工业实时操作系统及其在智…

xxx酒业有限责任公司突发环境事件应急预案WORD

导读&#xff1a;原文《xxx酒业有限责任公司突发环境事件应急预案word》&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。 目 录 1 总则 1.1 编制目的 1.2 编制…

JavaFx异常: Not on FX application thread; currentThread = Timer-0

我的定时器任务中有两个控件&#xff1a; FXML TextArea Display; FXML Label Label_Display; 执行下方代码会抛出&#xff1a;Exception in thread "Timer-0" java.lang.IllegalStateException: Not on FX application thread; currentThread Timer-0 Timer_tas…

企业信息查询,一站式数据服务

企业信息查询对销售人员是具有非常重要的意义&#xff0c;通过查询客户的基本信息&#xff0c;将这些转化为筛选客户的标准&#xff0c;这一过程可以理解为拓客。市面上这么多常见查询方法&#xff0c;如&#xff1a;搜索引擎查询&#xff08;百度、搜狗等&#xff09;、企业黄…

数字万用表测量基础知识--使用DMM测量电压

概览 DMM&#xff08;即数字万用表&#xff09;是一种电气测试和测量仪器&#xff0c;可测量直流和交流信号的电压、电流和电阻。本文介绍如何正确使用和理解数字万用表(DMM)。 使用DMM测量电压 实际上&#xff0c;每个DMM都具有直流和交流测量功能。电压测试通常用于测试和验…

命名实体识别方法:W2NER

一、介绍 代码:https://github.com/ljynlp/W2NER 论文:Unified Named Entity Recognition as Word-Word Relation Classification(武汉大学) 在平铺实体、重叠实体、非连续实体的数据集上,取得了SOTA的效果 建模方式:word-word的关系分类(中文:字-字的关系分类) …

编程中的宝藏:二分查找

二分查找 假设你需要在电话簿中找到一个以字母 “K” 开头的名字&#xff08;虽然现在谁还在用电话簿呢&#xff01;&#xff09;。你可以从头开始翻页&#xff0c;直到进入以 “K” 打头的部分。然而&#xff0c;更明智的方法是从中间开始&#xff0c;因为你知道以 “K” 打头…

【深度学习】多粒度、多尺度、多源融合和多模态融合的区别

多粒度&#xff08;multiresolution&#xff09;和多尺度&#xff08;multiscale&#xff09; 多粒度&#xff08;multiresolution&#xff09;和多尺度&#xff08;multiscale&#xff09;都是指在不同的空间或时间尺度上对数据或信号进行分析和处理。其中 多尺度&#xff1…

代码审计-Thinkphp框架审计前置知识点

代码审计必备知识点&#xff1a; 1、代码审计开始前准备&#xff1a; 环境搭建使用&#xff0c;工具插件安装使用&#xff0c;掌握各种漏洞原理及利用,代码开发类知识点。 2、代码审计前信息收集&#xff1a; 审计目标的程序名&#xff0c;版本&#xff0c;当前环境(系统,中间件…

接口测试实战,Jmeter正则提取响应数据-详细整理,一篇打通...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 在测试时&#xf…

Linux 编译CEF源码详细记录

Linux CEF&#xff08;Chromium Embedded Framework&#xff09;源码下载编译 背景 由于CEF默认的二进制分发包不支持音视频播放&#xff0c;需要自行编译源码&#xff0c;将ffmpeg开关打开才能支持。这里介绍的是Linux平台下的CEF源码下载编译过程。 Windows平台参考&#…

营收、净利同比微增,喜临门品牌升级“临门一脚”?

8月8日晚&#xff0c;喜临门发布2023上半年业绩报告。根据财报&#xff0c;2023年上半年&#xff0c;喜临门营业收入约38.05亿元&#xff0c;同比增加5.53%&#xff1b;归属于上市公司股东的净利润约2.22亿元&#xff0c;同比增加1.2%。 如果仅从这份财报看&#xff0c;喜临门…

高忆管理:“T+0”或带来市场波动

据了解&#xff0c;各地区对日内“T0”买卖&#xff0c;制定了不同的约束和监督办法。如美国不同账户类型监管、日本的特别涨跌停准则和日内单次“T0”、印度组织投资者禁入和特别保证金准则、中国台湾从信用账户到现股账户逐渐放开“T0”准则等。 上海高忆私募基金&#xff08…

软件的帮助文档应该怎么做?

在当今数字化时代&#xff0c;软件已经成为我们生活和工作中不可或缺的一部分。然而&#xff0c;对于用户来说&#xff0c;使用新的软件可能会面临一些挑战。这时&#xff0c;一个好的软件帮助文档就显得尤为重要了。软件帮助文档是一种向用户提供指导、解答问题和提供支的文件…