Boost开发指南-4.3optional

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"); //初始化为字符串testcout << *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.hqwc.cn/news/61180.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

75. 颜色分类

题目链接&#xff1a;力扣 解题思路&#xff1a;因为整个nums数组中只有0&#xff0c;1&#xff0c;2三个数组成。对nums升序排序后&#xff0c;0一定都在数组的最左边&#xff0c;2一定都在数组的最右边&#xff0c;1在数组的中间。那么只需要将0移动到数组的左边&#xff0c;…

基于低代码和数字孪生技术的电力运维平台设计

电力能源服务商在为用能企业提供线上服务的时候&#xff0c;不可避免要面对用能企业的各种个性化需求。如果这些需求和想法都要靠平台厂家研发人员来实现&#xff0c;那在周期、成本、效果上都将是无法满足服务运营需要的&#xff0c;这也是目前很多线上能源云平台应用效果不理…

React使用antd的图片预览组件,点击哪个图片就预览哪个的设置

使用了官方推荐的相册模式的预览&#xff0c;但是点击预览之后&#xff0c;每次都是从图片列表的第一张开始预览&#xff0c;而不是点击哪张就从哪张开始预览&#xff1a; 所以这里我就封装了一下&#xff0c;对初始化预览的列表进行了逻辑处理&#xff1a; 当点击开始预览的…

Spring Boot3.0(一):入门篇

什么是 Spring Boot Spring Boot 是由 Pivotal 团队提供的全新框架&#xff0c;其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置&#xff0c;从而使开发人员不再需要定义样板化的配置。 用我的话来理解&#xff0c;就是 Spring…

Nginx服务器上安装SSL证书

安全在网络世界中发挥着巨大作用&#xff0c;SSL安装过程需要高度集中精力。安装取决于服务器的类型。SSL证书是一种包含编码数据的文本文件&#xff0c;安装证书后&#xff0c;您的服务器将使用该文件。 在下面的文章中&#xff0c;我们重点介绍了SSL证书在Nginx服务器上的安…

四项代表厂商,Kyligence 入选 Gartner 数据及人工智能相关领域多项报告

近日&#xff0c;全球权威的技术研究与咨询公司 Gartner 发布了《2023 年中国数据、分析及人工智能技术成熟度曲线》、《2023 年分析与商业智能技术成熟度曲线报告》、《2023 年数据管理技术成熟度曲线报告》&#xff0c;Kyligence 分别入选这三项报告的指标平台 Metrics Store…

【Docker】Windows下docker环境搭建及解决使用非官方终端时的连接问题

目录 背景 Windows Docker 安装 安装docker toolbox cmder 解决cmder 连接失败问题 资料获取方法 背景 时常有容器方面的需求&#xff0c;经常构建调试导致测试环境有些混乱&#xff0c;所以想在本地构建一套环境&#xff0c;镜像调试稳定后再放到测试环境中。 Windows …

超低功耗在智能门锁行业的应用

1. 名词解释 在本体上以电子方式识别、处理人体生物特征信息、电子信息、网络通讯信息等并控制机械执行机构实施启闭的门锁”叫电子智能门锁。通俗地理解&#xff0c;智能门锁是电子信息技术与机械技术相结合的全新的锁具品类&#xff0c;是在传统机械锁基础上升级改进的&…

【uniapp】 软键盘弹出后fixed定位被顶上去问题

问题描述 当手机设计的导航栏为fixed定位上去时&#xff0c;输入框获取焦点就会把顶部自定义的导航栏顶到上面去&#xff0c;如下图所示 解决办法 输入框设置 :adjust-position“false” <input type"text" :adjust-position"false" focus"i…

Ubuntu18.04使用carla0.9.5联合仿真搭环境报错

Ubuntu18.04使用工程与carla0.9.5联合仿真报错 1 File "/home/cg/Auto_driving/src/ros-bridge/carla_ros_bridge/src/carla_ros_bridge/client.py", line 18, in <module>from carla_ros_bridge.bridge_with_rosbag import CarlaRosBridgeWithBagFile "…

实例035 动画形式的程序界面

实例说明 在很多的程序界面中&#xff0c;都是以菜单或工具栏的形式显示窗体界面&#xff0c;这种显示方式是以静止状态显示的&#xff0c;界面不够生动。下面介绍一个以动画显示窗体界面的设计方法。运行本例&#xff0c;效果如图1.35所示。 技术要点 在该实例中用到了Micr…

SketchUp Pro 2023 for Mac(草图大师)

SketchUp Pro 2023 for Mac提供了简单易学的用户界面和强大的工具集&#xff0c;使用户可以快速创建复杂的3D模型。其中包括智能绘图工具、自动生成几何体、高级纹理编辑器、实时阴影、交互式地形建模工具等功能。 一、创建形象化您的想法 手工绘制的乐趣。超级智能的3D建模软…