当前位置: 首页 > article >正文

std optional 的使用

std optional 的使用

文章目录

  • std optional 的使用
    • 1. `std::optional` 基本概念
    • 2. `std::optional` 的基本用法
      • 定义和初始化
      • 使用 `std::optional` 的成员函数
    • 3. `std::optional` 常用操作
    • 4. 使用 `std::optional` 的注意事项
    • 总结

std::optional 是 C++17 引入的一个标准库模板类,它提供了一种方式来表示一个值可能存在也可能不存在(即它的值是“可选的”)。它可以用于表示函数返回值可能为空的情况,避免直接使用指针或特殊值(如 nullptr-1)来表示缺失的值。

1. std::optional 基本概念

std::optional<T> 是一个模板类,其中 T 是存储类型。当 std::optional 对象有值时,您可以像普通对象一样访问它的值;如果它没有值,则它处于“无效”状态。

它提供了两种状态:

  • 有值:表示该 optional 存储了一个类型为 T 的值。
  • 无值:表示该 optional 没有值,可以认为它是“空的”或“未定义的”。

2. std::optional 的基本用法

定义和初始化

#include <iostream>
#include <optional>

std::optional<int> get_value(bool should_return_value) {
    if (should_return_value) {
        return 42;  // 有值
    } else {
        return std::nullopt;  // 没有值
    }
}

int main() {
    auto val1 = get_value(true);  // 有值
    auto val2 = get_value(false); // 没有值
    if (val1) {
        std::cout << "val1: " << *val1 << std::endl;  // 解引用获取值
    } else {
        std::cout << "val1 is empty" << std::endl;
    }
    if (val2) {
        std::cout << "val2: " << *val2 << std::endl;
    } else {
        std::cout << "val2 is empty" << std::endl;
    }
    return 0;
}

在这个例子中,get_value 函数根据 should_return_value 参数的值来返回一个 std::optional<int>。如果返回值存在,就通过 return 42 返回一个有值的 optional;如果返回值不存在,使用 std::nullopt 表示返回一个没有值的 optional

使用 std::optional 的成员函数

std::optional 提供了许多成员函数用于操作 optional 对象:

  • has_value():检查 optional 是否有值。返回 true 表示有值,false 表示没有值。
  • value():如果 optional 有值,返回存储的值;否则抛出 std::bad_optional_access 异常。
  • value_or(T&& default_value):如果 optional 有值,返回存储的值;否则返回提供的默认值。
  • operator*operator->:可以解引用 optional 以访问存储的值。
std::optional<int> maybe_value = 10;

if (maybe_value.has_value()) {
    std::cout << "Value: " << maybe_value.value() << std::endl;
} else {
    std::cout << "No value!" << std::endl;
}

std::optional<int> empty_value;
std::cout << "Value or default: " << empty_value.value_or(42) << std::endl;  // 输出 42

3. std::optional 常用操作

  • 初始化
    • std::optional<T> opt; // 默认构造,表示没有值
    • std::optional<T> opt = T(10); // 使用值初始化,表示有值
    • std::optional<T> opt = std::nullopt; // 无值
  • 检查是否有值
if (opt) {  // 或者 if (opt.has_value())
    std::cout << *opt << std::endl;
}
  • 解引用和访问值
if (opt) {
    std::cout << *opt << std::endl;  // 解引用
}
  • 提供默认值
int value = opt.value_or(100);  // 如果没有值,返回 100
  • 重置值
opt.reset();  // 使 opt 变为没有值的状态

4. 使用 std::optional 的注意事项

  • 访问值时要小心std::optional 可能没有值,直接访问没有值的 optional(例如,使用 optopt.value())会导致未定义行为或抛出异常。因此,通常需要先检查 optional 是否有值。
if (opt) {
    std::cout << *opt << std::endl;
} else {
    std::cout << "opt does not have a value" << std::endl;
}
  • 使用默认值: 使用 value_or 可以提供一个默认值,避免异常抛出或访问无值的情况。
int value = opt.value_or(42);  // 如果没有值,则返回 42
  • 性能考虑: 使用 std::optional 会有一些额外的内存开销,因为它需要在内部存储一个标志来指示是否有值。对于基本类型,optional 会比直接使用值类型稍微慢一些。对于大型对象或者需要多次检查值的场景,可以考虑优化设计,减少不必要的 optional 使用。
  • 与指针的比较std::optional<T> 与指针(如 std::unique_ptr<T> 或裸指针)有所不同。optional 只是表示一个值的“缺席”,而指针可能指向 nullptr 或指向一个对象。在设计中,根据需要选择是否使用 optional 或指针。
  • 不能用作容器的成员: 由于 std::optional 本身是一个包含类型的容器,不能将它作为容器成员来存储其他对象(例如 std::vector<std::optional<T>>),否则会增加额外的内存开销和复杂度。可以考虑其他方式来存储可选类型。

总结

std::optional 是 C++17 中非常有用的工具,能够优雅地处理“可能无值”的情况。它通过明确的 API 提供了检查值存在与否、访问值、提供默认值等功能,减少了使用裸指针或特殊值来表示缺失值的错误。使用时要注意检查 optional 是否有值,避免访问无值的状态导致的异常或错误。


http://www.kler.cn/a/464477.html

相关文章:

  • 论文泛读《LPFHE: Low-Complexity Polynomial CNNs for Secure Inference over FHE》
  • 【C#深度学习之路】如何使用C#实现Yolo5/8/11全尺寸模型的训练和推理
  • OWASP ZAP之API 请求基础知识
  • 2025.01.02(数据库)
  • 《Vue进阶教程》第三十四课:toRefs的使用
  • kafka开机自启失败问题处理
  • Redis--高可用(主从复制、哨兵模式、分片集群)
  • commit 错分支的一些补救操作
  • uni-app 多平台分享实现指南
  • 【Unity3D】ECS入门学习(十)NativeContainer、EntityCommandBuffer、CompleteDependency
  • el-table树形懒加载展开改为点击行展开
  • SAP财务凭证的更改、冲销的方式
  • python: generate model and DAL using Oracle
  • 【从零开始入门unity游戏开发之——C#篇43】C#补充知识——值类型和引用类型汇总补充、变量的生命周期与性能优化、值类型和引用类型组合使用
  • 虚拟路由冗余协议VRRP(Virtual Router Redundancy Protocol)
  • Springboot使用RabbitMQ实现关闭超时订单的一个简单示例
  • 【面试】深入理解 JavaScript 中的 Object.freeze()
  • k8s部署juicefs
  • SpringBoot教程(十四) SpringBoot之集成Redis
  • Vue 全局事件总线:Vue 2 vs Vue 3 实现
  • 一条SQL语句是如何执行的
  • BOC调制信号matlab性能仿真分析,对比功率谱,自相关性以及抗干扰性
  • python学opencv|读取图像(二十三)使用cv2.putText()绘制文字
  • 嵌入式驱动开发详解8(阻塞/非阻塞/异步通信)
  • Dokcer部署双主Mysql
  • XDOJ 771 求二叉树高度