Boost开发指南-4.3optional

这篇具有很好参考价值的文章主要介绍了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"); //初始化为字符串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();
}

Boost开发指南-4.3optional,Boost,c++文章来源地址https://www.toymoban.com/news/detail-638276.html

到了这里,关于Boost开发指南-4.3optional的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • GPT应用开发:GPT插件开发指南

    欢迎阅读本系列文章!我将带你一起探索如何利用OpenAI API开发GPT应用。无论你是编程新手还是资深开发者,都能在这里获得灵感和收获。 本文,我们将继续展示聊天API中插件的使用方法,让你能够轻松驾驭这个强大的工具。 首先给大家展示下插件的运行效果,如下图所示:

    2024年01月19日
    浏览(97)
  • 一招解决开发环境问题——远程容器开发指南

    使用C++作为主要开发语言的程序猿们应该会认同搭建开发环境是一件烦人的事情。为了编译一个程序不仅需要下载各种依赖包,还可能面临本地系统不兼容、编译器版本不一致、包版本冲突等各种问题。笔者在运营iLogtail开源社区的过程中发现开发和调试环境问题也是成员问

    2024年01月16日
    浏览(54)
  • 智能合约平台开发指南

    随着区块链技术的普及,智能合约平台已经成为了这个领域的一个重要趋势。智能合约可以自动化执行合同条款,大大减少了执行和监督合同条款所需的成本和时间。那么,如何开发一个智能合约平台呢?以下是一些关键步骤。 一、选择合适的区块链平台 智能合约通常运行

    2024年04月14日
    浏览(35)
  • ZEPHYR 快速开发指南

    国内小伙伴在学习zephyr的时候,有以下几个痛点: 学习门槛过高 github访问不畅,下载起来比较费劲。 这篇文章将我自己踩的坑介绍一下,顺便给大家优化一些地方,避免掉所有的坑。 首先用 virtualbox 来安装一个ubuntu22.04 本文适应的是ubuntu的操作系统 github.com 有时候无法访问

    2024年02月15日
    浏览(48)
  • kubernetes开发指南

    云原生的迅速崛起让越来越多开发者进入容器领域,作为云原生的核心项目kubernetes更是从业者最需要掌握的一门技术,市场上关于使用和入门类书籍非常之多,但是开发类书籍基本还是一个空缺,同样社区的官方文档也少之又少。本书希望为开发提供一定的指导作用,在开发

    2023年04月23日
    浏览(44)
  • LuatOS 开发指南

    下载软件 下载官方NDK例程压缩包到本地,并解压。可以看到目录如下: doc: 文档教程 env: 编译环境 example: NDK示例 platform: 需要编译的平台(air72x/air8xx) tools: 其他辅助软件 VSCode 使用VSCode打开NDK文件夹,并可以在VSCode中进行代码的编写和编译。 编译 打开终端,并进入到 exa

    2024年02月10日
    浏览(54)
  • 服务器(容器)开发指南——SSH打洞开发

    在进行定制化的服务开发时,我们有时候只能在固定的服务器上进行服务的开发。此时,通过命令行的方式进行开发的难度较大。我们可以考虑通过SSH打洞的方式,通过本地IDE的SSH连接功能来获取远程的环境进行代码的开发修改。 随着容器化技术的发展,越来越多的产品服务

    2024年02月15日
    浏览(45)
  • BFF网关模式开发指南

    BFF是近些年新衍生出来的一种开发模式,或者说是一种适配模式的系统,BFF全称为Backend OF Front意为后端的前端,为了适配微服务模式下前端后端系统接口调用混乱而出现的。在如今微服务盛行的趋势下,大型系统中划分出了数十个服务模块,例如商品,门店,运费,红包,订

    2024年02月13日
    浏览(43)
  • PyQGIS二次开发指南

    当你的数据处理使用的是Python语言,而你的导师又让你开发界面,那么PyQGIS二次开发指南是你必读的圣经。QGIS支持Python语言进行二次开发,你将学会如何使用Qt Designer进行界面设计、加载栅格数据、加载矢量数据、软件打包、安装包制作等。 随着GIS应用在国内的逐渐增多,越

    2024年04月27日
    浏览(38)
  • ArmSom---SPI开发指南

    RK3588从入门到精通 本⽂主要介绍在Rockchip平台配置spi接口并且使用的方法 开发板:ArmSoM-W3 Kernel:5.10.160 OS:Debian11 SPI(Serial Peripheral Interface),即串行外围设备接口,是一种同步的,全双工的,多设备的,多主机的通信协议,用于连接外围设备,如ADC、DAC、数据存储器、定时

    2024年02月05日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包