标准库标头 <optional> (C++17)学习之optional

news2024/11/13 15:05:17

类模板 std::optional 管理一个可选 的所含值,即既可以存在也可以不存在的值。

一种常见的 optional 使用情况是作为可能失败的函数的返回值。与如 std::pair<T, bool> 等其他手段相比,optional 可以很好地处理构造开销高昂的对象,并更加可读,因为它明确表达了意图。

optional<T> 的任何实例在任意给定时间点要么含值,要么不含值

如果一个 optional<T> 含值,那么保证该值作为 optional 对象所用空间的一部分分配,即不会发生动态内存分配。因此,optional 对象模拟的是对象而非指针,尽管定义了 operator*() 和 operator->() 运算符。

当一个 optional<T> 类型的对象被按语境转换到 bool 时,若对象含值 则转换返回 true,若它不含值" 则返回 false。

optional 对象在下列条件下含值

  • 对象被以 T 类型的值或另一含值 的 optional 初始化/赋值。

对象在下列条件下不含值

  • 对象被默认初始化。
  • 对象被以 std::nullopt_t 类型的值或不含值 的 optional 对象初始化/赋值。
  • 调用了成员函数 reset()。

不存在可选的引用、函数、数组或 cv void:如果以这些类型实例化 optional,那么程序非良构。另外,如果以(可有 cv 限定的)标签类型 std::nullopt_t 或 std::in_place_t 实例化 optional,那么程序非良构。

成员函数

(构造函数)

构造 optional 对象
(公开成员函数)

(析构函数)

销毁容纳的值(如果存在)
(公开成员函数)

operator=

对内容赋值
(公开成员函数)
观察器

operator->operator*

访问所含值
(公开成员函数)

operator boolhas_value

检查对象是否含值
(公开成员函数)

value

返回所含值
(公开成员函数)

value_or

在所含值可用时返回它,否则返回另一个值
(公开成员函数)
修改器

swap

交换内容
(公开成员函数)

reset

销毁任何所含值
(公开成员函数)

emplace

原位构造所含值
(公开成员函数)

非成员函数

make_optional

(C++17)

创建一个 optional 对象
(函数模板)

示例代码:

#include <iostream>
#include <optional>
#include <string>
#include <iomanip>
#include <cstdlib>
#include <vector>

#pragma warning(disable:4996)





// optional 可用作可能失败的工厂的返回类型
std::optional<std::string> create(bool b)
{
    if (b)
        return "Godzilla";
    return {};
}

// 能用 std::nullopt 创建任何(空的)std::optional
auto create2(bool b)
{
    return b ? std::optional<std::string>{"Godzilla"} : std::nullopt;
}

std::optional<const char*> maybe_getenv(const char* n)
{
    if (const char* x = std::getenv(n))
        return x;
    else
        return {};
}

struct A
{
    std::string s;
    A(std::string str) : s(std::move(str)) { std::cout << " 已构造\n"; }
    ~A() { std::cout << " 已析构\n"; }
    A(const A& o) : s(o.s) { std::cout << " 被复制构造\n"; }
    A(A&& o) : s(std::move(o.s)) { std::cout << " 被移动构造\n"; }

    A& operator=(const A& other)
    {
        s = other.s;
        std::cout << " 被复制赋值\n";
        return *this;
    }

    A& operator=(A&& other)
    {
        s = std::move(other.s);
        std::cout << " 被移动赋值\n";
        return *this;
    }
};

struct B
{
    std::string s;

    B(std::string str) : s(std::move(str)), id{ n++ } { note("+ 构造"); }
    ~B() { note("~ 析构"); }
    B(const B& o) : s(o.s), id{ n++ } { note("+ 复制构造"); }
    B(B&& o) : s(std::move(o.s)), id{ n++ } { note("+ 移动构造"); }

    B& operator=(const B& other)
    {
        s = other.s;
        note("= 复制赋值");
        return *this;
    }

    B& operator=(B&& other)
    {
        s = std::move(other.s);
        note("= 移动赋值");
        return *this;
    }

    inline static int n{};
    int id{};
    void note(std::string s) { std::cout << "  " << s << " #" << id << '\n'; }
};



int main()
{
    std::cout << "create(false) 返回 "
        << create(false).value_or("empty") << '\n';

    // 返回 optional 的工厂函数可用作 while 和 if 的条件
    if (auto str = create2(true))
        std::cout << "create2(true) 返回 " << *str << '\n';

    //operator= example 
    std::optional<const char*> s1 = "abcefg", s2; // 构造函数
    s2 = s1; // 赋值
    s1 = "hijklm"; // 衰变赋值(U = char[4], T = const char*)
    std::cout << *s2 << ' ' << *s1 << '\n';

    //std::optional<T>::operator->, std::optional<T>::operator*  example
    using namespace std::string_literals;
    std::optional<int> opt1 = 1;
    std::cout << "opt1: " << *opt1 << '\n';

    *opt1 = 2;
    std::cout << "opt1: " << *opt1 << '\n';

    std::optional<std::string> opt2 = "abc"s;
    std::cout << "opt2: " << std::quoted(*opt2) << ", size: " << opt2->size() << '\n';

    // 你能通过在到 optional 的右值上调用 operator* “取”其所含值
    auto taken = *std::move(opt2);
    std::cout << "taken: " << std::quoted(taken) << "\n"
        "opt2: " << std::quoted(*opt2) << ", size: " << opt2->size() << '\n';

    //std::optional<T>::operator bool, std::optional<T>::has_value example
    std::cout << std::boolalpha;
    std::optional<int> opt;
    std::cout << opt.has_value() << '\n';
    opt = 43;
    if (opt)
        std::cout << "设置值为 " << opt.value() << '\n';
    else
        std::cout << "未设置值\n";

    opt.reset();
    if (opt.has_value())
        std::cout << "值仍被设为 " << opt.value() << '\n';
    else
        std::cout << "不再设置值\n";

    //std::optional<T>::value example
    std::optional<int> opt3 = {};

    try
    {
        [[maybe_unused]] int n = opt3.value();
    }
    catch (const std::bad_optional_access& e)
    {
        std::cout << e.what() << '\n';
    }

    try
    {
        opt3.value() = 42;
    }
    catch (const std::bad_optional_access& e)
    {
        std::cout << e.what() << '\n';
    }

    opt3 = 43;
    std::cout << *opt3 << '\n';

    opt3.value() = 44;
    std::cout << opt3.value() << '\n';

    //std::optional<T>::value_or  example
    std::cout << maybe_getenv("SHELL").value_or("(none)") << '\n';
    std::cout << maybe_getenv("MYPWD").value_or("(none)") << '\n';

    //std::optional<T>::swap example
    std::optional<std::string> opt4("First example text");
    std::optional<std::string> opt5("2nd text");

    enum Swap { Before, After };
    auto print_opts = [&](Swap e) {
        std::cout << (e == Before ? "交换前:\n" : "交换后:\n");
        std::cout << "opt1 含有 '" << opt4.value_or("") << "'\n";
        std::cout << "opt2 含有 '" << opt5.value_or("") << "'\n";
        std::cout << (e == Before ? "---SWAP---\n" : "\n");
        };

    print_opts(Before);
    opt4.swap(opt5);
    print_opts(After);

    // 在仅一者含值时交换
    opt4 = "Lorem ipsum dolor sit amet, consectetur tincidunt.";
    opt5.reset();

    print_opts(Before);
    opt4.swap(opt5);
    print_opts(After);

    //std::optional<T>::reset example
    std::cout << "创建空 optional:\n";
    std::optional<A> opt6;

    std::cout << "创建并赋值:\n";
    opt6 = A("Lorem ipsum dolor sit amet, consectetur adipiscing elit nec.");

    std::cout << "重置 optional:\n";
    opt6.reset();
    std::cout << "A示例结束\n";

    //emaplace example
    std::optional<B> opt7;

    std::cout << "赋值:\n";
    opt7 = B("Lorem ipsum dolor sit amet, consectetur adipiscing elit nec.");

    std::cout << "放置:\n";
    // 由于 opt 含值,这亦将销毁该值
    opt7.emplace("Lorem ipsum dolor sit amet, consectetur efficitur. ");

    std::cout << "B示例结束\n";

    //std::make_optional example
    auto op8 = std::make_optional<std::vector<char>>({ 'a','b','c' });
    std::cout << "op1: ";
    for (char c : op8.value())
        std::cout << c << ',';
    auto op9 = std::make_optional<std::vector<int>>(5, 2);
    std::cout << "\nop2: ";
    for (int i : *op9)
        std::cout << i << ',';
    std::string str{ "hello world" };
    auto op10 = std::make_optional<std::string>(std::move(str));
    std::cout << "\nop3: " << quoted(op10.value_or("empty value")) << '\n';
    std::cout << "str: " << std::quoted(str) << '\n';


}



运行结果:

参考:

std::optional - cppreference.com

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

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

相关文章

【科普】双轴测径仪是根据哪个测量值控制外径尺寸?

单轴测径仪与双轴测径仪都是自带闭环控制功能的在线外径测量设备&#xff0c;单轴测径仪只有一个测头&#xff0c;是根据该测头的检测数据进行控制&#xff0c;这点毋庸置疑&#xff0c;那双轴测径仪这种具备两组测头的设备又是如何控制的&#xff0c;本文就来简单的介绍一下。…

Ubuntu安装网卡驱动

没有无线网 给自己装了双系统后&#xff0c;发现没有无线网络 下载驱动文件 打开终端&#xff0c;输入 lspci -k 能看到&#xff0c;虽然我是RTL8125BG&#xff0c;但use的是r8169: 08:00.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL8125 2.5GbE Controll…

Vue前端路由详解——以Ruoyi框架为案例学习

Vue路由 Vue路由详解_vue 页面路由-CSDN博客 路由模式 Vue 的路由模式&#xff1a;hash 模式和 history 模式的区别_vue路由history和hash的区别-CSDN博客 URL格式&#xff1a; Hash模式&#xff1a;URL中包含#号&#xff0c;用于区分页面部分&#xff0c;实际请求的页面地址…

OpenCV下的无标定校正(stereoRectifyUncalibrated)

OpenCV下的无标定校正(stereoRectifyUncalibrated) 文章目录 1. 杂话2. 无标定校正2.1 先看代码2.2 一点解释2.3 findFundamentalMat参数2.4 stereoRectifyUncalibrated参数 3. 矫正结果 1. 杂话 咱们在之前的帖子里面讲了一些比较常规的标定和校正OpenCV下的单目标定&#xff…

Unity数据持久化 之 文件操作(增删查改)

本文仅作笔记学习和分享&#xff0c;不用做任何商业用途 本文包括但不限于unity官方手册&#xff0c;unity唐老狮等教程知识&#xff0c;如有不足还请斧正​​ 这里需要弄清几个概念&#xff1a; File&#xff1a;提供文件操作的静态方法&#xff0c;是管理的 Windows.File -…

除浮毛用吸尘器有用吗?除浮毛真正有用浮毛空气净化器总结

我的医生朋友经常给朋友们讲解宠物毛发对呼吸道的潜在影响&#xff0c;这引起了不同的反应。有人采纳了他的建议&#xff0c;采取了防护措施&#xff1b;而有人则认为他在制造恐慌&#xff0c;特别是当听到宠物医生的说法与之相左时。 我曾也心存疑虑&#xff0c;但经过与朋友…

做开发一年多了,分享一下自己的疑惑以及大模型给我的一些建议~

写在最前面,下面的疑问是我自己的一些困惑和想知道背后的答案,回答这块是大模型的一些建议,我觉得对我来说不能说很对,至少给我了启发和思考,分享出来给大家,大家如果也有类似的疑惑,希望能提供到帮助 原先Java生态是出现各种复杂的业务场景,需要使用合理且合适的技术架…

house of cat

文章目录 house of cat概述&#xff1a;_IO_wfile_jumps进入_IO_wfile_seekoffFSOP__malloc_assert 例题&#xff1a;思路&#xff1a;分析&#xff1a;利用&#xff1a; house of cat 概述&#xff1a; house of cat主要的摸底还是覆盖vtable指针&#xff0c;因为在glibc-2.2…

结构型设计模式—桥接模式

结构型设计模式—桥接模式 欢迎长按图片加好友&#xff0c;我会第一时间和你分享持续更多的开发知识&#xff0c;面试资源&#xff0c;学习方法等等。 假设你要买一张新桌子&#xff0c;你有两个选择&#xff1a;一种是木制的桌子&#xff0c;另一种是金属制的桌子。 无论你选…

软件工程知识点总结(1):软件工程概述

1 什么是软件&#xff1f; 定义&#xff1a;计算机系统中的程序及其文档。 ——程序是计算机任务的处理对象和处理规模的描述&#xff1b; ——文档是为了便于了解程序所需要的阐明性资料。 2 软件的特点&#xff1f; 软件是无形的&#xff0c;不可见的逻辑实体 ——它的正确与…

【Python基础】字典类型

本文收录于 《Python编程入门》专栏&#xff0c;从零基础开始&#xff0c;分享一些Python编程基础知识&#xff0c;欢迎关注&#xff0c;谢谢&#xff01; 文章目录 一、前言二、Python 字典类型2.1 访问字典里的值2.2 修改字典2.3 删除字典元素2.4 字典键值的特性2.5 遍历字典…

免费pdf转word软件,为你整理出8种方法,总有一个适合你

在日常办公和学习中&#xff0c;PDF文档因其格式稳定、不易修改的特性而广受欢迎。然而&#xff0c;有时我们需要对PDF内容进行编辑或格式调整&#xff0c;这时将其转换为Word文档便显得尤为重要。下面给大家介绍8种将PDF转换成Word的方法&#xff0c;包括在线网站、专业软件及…

第四篇——数学思维:数学家如何从逻辑出发想问题?

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 数学思维中的很多方法能够让我们脱离事物的表象去直击本质通过本质进行逻…

在Linux中使用MySQL基础SQL语句及校验规则

卸载内置环境 查看是否存在MySQL ps axj | grep mysql关闭MySQL systemctl stop mysqld MySQL对应的安装文件 rpm -qa | grep mysql 批量卸载 rpm -qa | grep mysql | xargs yum -y remove 上传MySQL rz 查看本地yum源 ls /etc/yum.repos.d/ -a 安装MySQL rpm -ivh…

Linux:手搓shell

之前学了一些和进程有关的特性&#xff0c;什么进程控制啊进程替换啊&#xff0c;我们来尝试自己搓一个shell()吧 首先我们观察shell的界面&#xff0c;发现centos的界面上有命令提示符&#xff1a; [主机名用户名当前路径] 我们可以通过调用系统函数获取当前路径&#xff0…

C语言代码练习(第十二天)

今日练习&#xff1a; 28、&#xff08;指针&#xff09;将字符串 a 复制为字符串 b &#xff0c;然后输出字符串 b 29、改变指针变量的值 30、输入两个整数&#xff0c;然后让用户选择1或者2&#xff0c;选择1是调用 max &#xff0c;输出两者中的大数&#xff0c;选择2是调用…

Mac M1 安装Hadoop教程(安装包安装)

一、引言 前面一期&#xff0c;我分享了通过homebrew方式安装Hadoop&#xff0c;本期我将通过安装包方式介绍下hadoop如何安装。二、下载open jdk8 官方下载地址 注意如果是x86架构的苹果电脑&#xff0c;Architecture选择x86 64-bit或者 x86-32bit。 下载后&#xff0c;将得…

Axios前后端对接

前端&#xff1a; 通过GET获取元素&#xff1a; console.log(res),接收接口返回的数据并打印出来。 async:是异步的知识。 通过POST修改&#xff0c;更新元素&#xff1a; 后端&#xff1a; 通过前端从后端获取一个对象&#xff1a; 后端执行相应方法&#xff1a; 然后获取L…

Spring6学习笔记2:容器IoC

文章目录 3 容器&#xff1a;IoC3.1 IoC容器3.1.2 依赖注入3.1.3 IoC容器在Spring的实现 3.2 基于XML管理Bean3.2.1 搭建子模块spring6-ioc-xml3.2.2 实验一&#xff1a;获取bean①方式一&#xff1a;根据id获取②方式二&#xff1a;根据类型获取③方式三&#xff1a;根据id和类…

day-48 一个小组的最大实力值

思路 想把所有非零数相乘&#xff0c;再统计负数的个数&#xff0c;如果负数为奇数个&#xff0c;则把乘机除以最大的那个负数即为答案&#xff0c;如果为偶数个&#xff0c;那么乘机即为答案 解题过程 但要考虑特殊情况&#xff1a;1.只有零和一个负数&#xff0c;返回零 2.全…