一、Optional 的历史

Optionals were first proposed for C++ in 2005.
Optional<T>, where T is constrained not to be a reference, was added in 2017.
Optionals for lvalue references were voted in at the Sofia meeting in June, 2025

理解:

  1. 2005 年:最初提出了 Optional 的概念,用于表示一个可能有值也可能没有值的对象
  2. 2017 年Optional<T> 正式加入 C++17 标准,但 T 不能是引用类型
    • std::optional<int> 是合法的,但 std::optional<int&> 当时不被允许。
  3. 2025 年 Sofia 会议:针对 左值引用 的 Optional(optional<T&>)被正式投票通过。

总结:

  • Optional = “可能存在的值”
  • 引用类型的 Optional 需要特殊处理,因为引用没有默认构造和拷贝语义。

二、早期背景与关注点

Speaker notes: early history, Boost.Optional and reference specialization

理解:

  • 早期历史
    • Boost.Optional 是 C++ 社区最初实现 Optional 的库
    • 早期尝试 specialize 对引用的 Optional
  • 问题
    • Optional 对引用的行为容易产生陷阱,尤其是赋值和生命周期问题(state-dependent assignment)

举例:

int x = 42;
std::optional<int&> o;  // 空 optional
o = x;                  // 赋值后 o 持有 x 的引用
x = 100;                // 通过 optional 访问时值变了

三、关键提案文档

- N1878: A Proposal to Add an Utility Class to Represent Optional Objects
- P1175R0: A Simple and Practical Optional Reference for C++

理解:

  • N1878:最早期 Optional 提案
  • P1175R0:为 Optional 引用提出了简洁实用的实现方案
    • 目标:在不破坏已有 Optional 的前提下,支持 optional<T&>

四、C++20 的尝试

Std:Optional reproposed reference support for C++20, which was not adopted

理解:

  • C++20 曾经尝试为 Optional 添加引用支持
  • 最终没有通过,原因:
    • 引用的赋值行为可能依赖于 Optional 是否有值(state-dependent)
    • 设计复杂性增加

五、后续发展

picked up the torch again in 2023, revision 9 design approved by Library Evolution Working Group, R13 adopted

理解:

  • 2023 年:重新提出 optional 引用支持的提案
  • Revision 9:设计被 Library Evolution Working Group 批准
  • R13:最终被采纳为标准

六、Optional 的 Range 支持

In 2024, the proposal to make optional a range
- P3168R0: Give Std:Optional Range Support
- P1683R0: References for Standard Library
- P2988R0

理解:

  • 2024 年:提出让 Optional 支持 range 接口
    • 不再单独区分 0 或 1 的元素
    • Optional 引用的实现和 views::maybe 测试用例被用来验证新接口
  • 意义
    • Optional 与 Range 更好整合
    • 同时通过实际实现获得社区反馈
    • 成为 Beman 库 的基础之一

可视化理解:
optional<T>→range supportrange of 0 or 1 element(s) \text{optional<T>} \xrightarrow{\text{range support}} \text{range of 0 or 1 element(s)} optional<T>range support range of 0 or 1 element(s)

七、总结与关键点

  1. Optional 对引用的标准化
    • 从 2005 到 2025,经历了多轮提案
    • C++17 中 optional<T> 不支持引用
    • 2025 年 Sofia 会议通过 optional<T&>
  2. 设计难点
    • 引用的生命周期
    • 状态依赖赋值行为
  3. 提案文档
    • N1878、P1175R0、P1683R0、P3168R0、P2988R0
  4. Range 支持
    • Optional 变成长度为 0 或 1 的范围
    • 统一接口,便于组合和泛型算法
  5. 实际效果
    • Optional 引用 + Range 接口
    • 提高标准库一致性
    • 为 Beman 库提供了可测试的实现和反馈

一、为什么花了很长时间?

The core of the difficulty has been that references are not values and types containing a reference do not have value semantics.
References do not fit comfortably in the C++ type system.

理解:

  1. 引用不是值
    • C++ 中 int&T& 是引用类型,本质上是别名,不拥有对象本身。
    • Optional 是值类型(value type),需要具备 值语义(value semantics)
      • 拷贝、赋值、移动应该保持一致行为
      • 拥有明确的生命周期
  2. 引用不符合值语义
    • 例如 std::optional<T&> 拷贝或赋值时,不能真正拷贝引用所指的对象,只能拷贝引用本身

困难的根源:类型系统里引用和值类型不一致

二、为什么不能直接用指针?

The core value semantic type that also has reference semantics is a pointer,
but pointers have underconstrained and unsafe semantics.

理解:

  • 指针(T*)有值语义,并可以指向对象
  • 但是指针有潜在问题:
    • 可能为空(nullptr
    • 可能悬空(dangling pointer)
    • 对指针的操作不安全

所以直接用指针替代引用无法满足 Optional 的安全设计要求

三、背后的设计讨论

The long discussion has been a proxy for what reference semantic types should
look like in value semantic types in the standard library,
particularly for "sum" types, like expected and variant.

理解:

  • 标准化 Optional<T&> 的讨论本质上是:
    • 如何把 引用语义(reference semantics) 安全地嵌入到 值语义类型(value semantic type)
  • 特别是对于 sum types(和类型选择相关的类型):
    • std::expected<T, E>
    • std::variant<Ts...>
  • 目标:在保证值语义安全的前提下,允许引用类型存在

四、Optional 快速回顾

An owning type, with value semantics, with one additional out-of-band value.

理解:

  • Optional 是一个拥有对象(owning type)
  • 具有值语义(可以复制、赋值、移动)
  • 并且多了一个额外的“空”状态(out-of-band value)
    • 用于表示没有值

数学上可以用 代数类型 来表示:

ALGEBRAICALLY
T + 1
  • 意思是:类型 T + 一个额外的“空”状态
  • C++ 实现上常用:
std::variant<T, std::monostate>
  • std::monostate 用作“没有值”的占位符

五、总结

  1. 核心难点
    • 引用不是值类型
    • 值语义类型不能直接包含引用
  2. 指针不是好方案
    • 虽然值语义 + 引用语义,但不安全
  3. 最终目标
    • 让 Optional、expected、variant 等 sum type 支持引用语义
    • 同时保持值语义安全
  4. 代数表示
    Optional<T>≅T+1≅std::variant<T, std::monostate> \text{Optional<T>} \cong T + 1 \cong \text{std::variant<T, std::monostate>} Optional<T>T+1std::variant<T, std::monostate>

一、核心用例 (Core Use Case)

int size;
if (optional<int> s = readConfigValue("Size")) {
    size = *s;
} else {
    size = 0;
}
return size;

理解:

  1. readConfigValue("Size") 返回一个 optional<int>
    • 如果配置中有 “Size”,就返回 optional<int> 包含该值
    • 如果没有,返回空 optional
  2. if (optional<int> s = ...) 语句用于判断是否有值
  3. *s 解引用获取值
  4. 否则用默认值 0

核心思想:Optional 可以安全地表示“有值或无值”状态

二、C++26 范围版本 (Range Version)

int size = 0;
for (int s : readConfigValue("Size")) {
    size = s;
}
return size;

理解:

  1. C++26 中,optional<T> 支持 范围 (range) 语义
    • 可以直接用于 for 循环
    • 如果 optional 有值,循环执行一次
    • 如果 optional 为空,循环不执行
  2. 优点:
    • 省去了显式 if 判断
    • 更简洁、表达式语义清晰

数学上可以理解为:
optional<T>∼零或一元素集合 T 或 空 \text{optional<T>} \sim \text{零或一元素集合 } { T \text{ 或 空} } optional<T>零或一元素集合 T  

  • 所以可以用 集合迭代 语义

三、默认 Optional 参数 (Default Optional Parameter)

constexpr int optParam(int a, optional<int> b = {}) {
    if (b) {
        return a + *b;
    }
    return a;
}
const auto t1 = optParam(3);    // b 为空
const auto t2 = optParam(3, 4); // b = 4
static_assert(t1 != t2);

理解:

  1. b 是一个 optional<int> 参数
    • 默认值为空 {}
    • 可以传递实际的值,例如 4
  2. 调用示例:
    • optParam(3)b 为空 → 返回 a = 3
    • optParam(3, 4)b 有值 → 返回 a + b = 7
  3. static_assert(t1 != t2);
    • 编译期验证两种情况的结果不同

优点:

  • Optional 可以安全地作为 默认参数
  • 可以用 单一函数 处理有值和无值情况

四、总结


用例 C++26 改进 解释
核心用例 if(optional) 显式判断是否有值
范围用例 for(int s : optional) Optional 可迭代,空 optional 循环不执行
默认参数 optional<int> b = {} 可以作为函数默认参数,简化函数调用

数学视角:
optional<T>∼T + 1(一个值或空) \text{optional<T>} \sim \text{T + 1} \quad (\text{一个值或空}) optional<T>T + 1(一个值或空)

  • 在迭代语义下:
    for x∈optional<T>⇒0次或1次循环 \text{for } x \in \text{optional<T>} \Rightarrow 0 \text{次或1次循环} for xoptional<T>0次或1次循环

一、optional<T&> 快速概览

A non-owning type,
with reference and value semantics,
with one additional value representing the empty state.

理解:

  1. 非拥有类型(Non-owning)
    • optional<T&> 不拥有引用对象本身,只是引用已有对象
  2. 具有引用和值语义(Reference & Value Semantics)
    • 可以像普通引用一样访问对象
    • 也可以像普通 optional 一样判断是否存在值
  3. 有一个特殊值表示空状态(Empty State)
    • 类似 optional<T>nullopt
    • 用于表示引用不存在

数学表示:
optional<T&>∼引用对象∪空 \text{optional<T\&>} \sim { \text{引用对象} } \cup {\text{空}} optional<T&>引用对象

二、核心用例:查找并修改 Map 中的元素

传统方式(Today)

auto i = map.find("one");
auto j = map.find("two");
auto k = map.find("three");
assert(i->second == 1);
assert(j->second == 2);
assert(k == map.end());

理解:

  1. 使用 map.find 返回迭代器
    • 如果找到了,迭代器指向元素
    • 如果没找到,返回 map.end()
  2. 核心问题:
    • 对未找到的元素访问需要判断 iterator != map.end()
    • 编码比较繁琐

使用 optional<int&> 的方式

optional<int&> i = findInMap("one");
optional<int&> j = findInMap("two");
optional<int&> k = findInMap("three");
EXPECT_TRUE(*i == 1);
EXPECT_TRUE(*j == 2);
EXPECT_TRUE(!k);
// 修改值
*(findInMap("one")) = 3;
EXPECT_TRUE(*i == 3);

理解:

  1. findInMap("one") 返回 optional<int&>
    • 如果找到返回引用
    • 如果未找到返回空
  2. 优点
    • 不再需要检查迭代器是否为 end()
    • 可以直接解引用修改值
    • 更加安全和简洁

数学表示:
optional<int&>={引用对象如果找到空如果未找到 \text{optional<int\&>} = \begin{cases} \text{引用对象} & \text{如果找到} \\ \text{空} & \text{如果未找到} \end{cases} optional<int&>={引用对象如果找到如果未找到

三、可选引用作为函数参数

void doSomething(std::string const& data,
                 optional<Logger&> logger = {}) {
    for (auto l : logger) {
        l.log(data);
    }
    return;
}

理解:

  1. loggeroptional<Logger&>
    • 默认值为空 {}
    • 表示可以没有 logger
  2. for (auto l : logger)
    • 遍历可选引用
    • 如果有值,循环一次
    • 如果为空,循环不执行
  3. 优点
    • 替代传统指针参数(Logger* logger = nullptr
    • 语义更清晰,避免空指针错误
    • 可以安全地调用 log(data)

数学上理解:
optional<T&>∼0 或 1 个元素集合 \text{optional<T\&>} \sim \text{0 或 1 个元素集合} optional<T&> 1 个元素集合

  • 用作范围(range)语义,遍历一次或不遍历

四、总结


用例 传统写法 optional<T&> 写法 优势
查找并读取 迭代器判断 optional<int&> 简洁、安全
修改值 迭代器解引用 optional<int&> 可以直接修改值
函数参数 指针参数 optional<Logger&> 安全、可遍历、不需检查空

总结一句话:
optional<T&> 把引用的存在/不存在状态变成了可操作的对象,既保留引用语义,又拥有 optional 的安全性和范围语义。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐