目录
-
-
- [14.1 类型安全(Type Safety)](#14.1 类型安全(Type Safety))
- [14.2 `std::optional`(C++17)](#14.2
std::optional(C++17)) -
- Monadic操作(C++23)
-
- [关键特点(为何用 Monadic 操作?)](#关键特点(为何用 Monadic 操作?))
- 实际场景示例(对应题目需求)
-
14.1 类型安全(Type Safety)
类型安全性:
指一种编程语言防止发生类型错误(typing errors)的能力范围或程度。
指一种编程语言对程序行为进行保障的程度。

例1:容器为空
cpp
void removeOddsFromEnd(vector<int>& vec){
while(vec.back() % 2 == 1){
vec.pop_back();
}
}
类型安全吗?
如果vec是空的怎么办?

未定义行为(Undefined Behavior):函数可能会崩溃,可能会返回无用的垃圾数据,也可能意外返回某个实际有效的值。
解决方案1:
cpp
void removeOddsFromEnd(vector<int>& vec){
while(!vec.empty() && vec.back() % 2 == 1){
vec.pop_back();
}
}
核心思想:确保向量(vec)非空这一前置条件,是程序员的职责;否则,程序将出现未定义行为!
例2:指针指向空
cpp
valueType& vector<valueType>::back(){
return *(begin() + size() - 1);
}
不验证指针是否指向实际内存就对其进行解引用,这种行为属于未定义行为!
cpp
valueType& vector<valueType>::back(){
if(empty()) throw std::out_of_range;
return *(begin() + size() - 1);
}
用std::pair解决
类型安全指函数签名对函数行为进行保障的程度。
cpp
valueType& vector<valueType>::back(){
return *(begin() + size() - 1);
}
vector 容器的 back() 函数存在 "签名承诺与实际情况不匹配" 的问题。从函数签名来看,back() 的返回类型被定义为 valueType&(即 valueType 类型的引用),这相当于向开发者 "承诺":调用该函数时,一定会返回一个有效的 valueType 类型数据(对应容器的最后一个元素)。但实际场景中,若容器为空(vec.empty() == true),"最后一个元素" 根本不存在。
cpp
std::pair<bool, valueType&> vector<valueType>::back(){
if(empty()){
return {false, valueType()}; // valueType()为默认构造函数
}
return {true, *(begin() + size() - 1)};
}
现在,back () 函数会明确告知(开发者):容器中可能存在也可能不存在最后一个元素。
但调用构造函数是资源昂贵的。
14.2 std::optional(C++17)
std::optional 是一个模板类,它要么包含一个 T 类型的值,要么不包含任何值(用 nullopt 表示)。
nullptr可转换为任意指针类型值的对象
nullopt可转换为任意 optional 类型值的对象
std::optional 类型包含以下方法:
.value()方法:
返回其包含的值;若 std::optional 未包含值,则抛出 bad_optional_access 异常。
.value_or(valueType val)方法:
返回其包含的值;若 std::optional 未包含值,则返回默认值(即参数 val)。
.has_value()方法:
若 std::optional 包含值,则返回 true;若未包含值,则返回 false。
补充说明(技术术语适配):
bad_optional_access:是 C++ 标准库中专门为std::optional设计的异常类型,中文通常直接保留原名(或译为"可选值访问异常"),用于明确提示"访问了无值的std::optional对象"这一错误场景。valueType val:此处valueType指std::optional模板类的泛型参数类型(如std::optional<int>中的int),val是开发者传入的"默认值",其类型需与valueType一致。
cpp
std::optional<valueType> vector<valueType>::back(){
if(empty()){
return {};
}
return *(begin() + size() - 1);
}
实例:
cpp
#include <iostream>
#include <optional>
std::optional<int> divide(int numerator, int denominator) {
if (denominator != 0) {
return numerator / denominator;
} else {
return std::nullopt;
}
}
int main() {
int a = 10;
int b = 2;
std::optional<int> result = divide(a, b);
if (result) {
std::cout << "Result: " << result.value() << std::endl;
} else {
std::cout << "Division by zero occurred." << std::endl;
}
result = divide(10, 0);
if (result) {
std::cout << "Result: " << result.value() << std::endl;
} else {
std::cout << "Division by zero occurred." << std::endl;
}
return 0;
}
这段代码展示了 C++ 中std::optional的用法,主要用于处理可能失败的操作(此处为除法运算)。
代码解析如下:
- 包含了必要的头文件:
iostream用于输入输出,optional提供std::optional类型支持。 - 定义了
divide函数,返回类型为std::optional<int>:
- 当分母不为 0 时,返回除法结果(包装在
std::optional中) - 当分母为 0 时,返回
std::nullopt表示操作失败
在main函数中:
- 首先计算 10 除以 2,结果有效,输出计算结果
- 然后尝试计算 10 除以 0,操作失败,输出错误信息
std::optional的优势在于:
- 明确表示一个操作可能成功或失败
- 避免使用特殊值(如 - 1)来表示错误,使代码更清晰
- 通过
if (result)可以便捷地检查操作是否成功 - 使用
result.value()安全地获取结果(前提是已检查操作成功)
在 C++ 的 std::optional 语境中,Monadic Operations(单子操作) 是一组用于安全、链式处理"可能存在或不存在的值"(即 std::optional 包裹的值)的成员函数,核心目的是避免显式条件判断,如 if,同时确保代码简洁且无空指针风险。其本质是通过"链式调用"自动处理"值存在"和"值不存在"两种分支,仅在值有效时执行后续逻辑,无效时直接跳过并传递"无值"状态。
Monadic操作(C++23)
文档
std::optional 提供的核心 Monadic 操作(C++23)有 3 种,各自分工明确:
| 操作函数 | 核心作用 | 输入函数要求 | 行为逻辑 |
|---|---|---|---|
transform |
转换值 :若有值,对值执行"无副作用的转换",结果仍包裹在optional中 |
输入:T(原始值类型);输出:U(转换后类型) |
有值:调用函数转换值,返回optional;无值:直接返回nullopt |
and_then |
链式依赖 :若有值,执行"返回新optional的逻辑"(如后续查询) |
输入:T;输出:optional |
有值:调用函数获取新optional,返回该结果;无值:直接返回nullopt |
or_else |
兜底处理 :若无值,执行"生成兜底optional"的逻辑 |
输入:无;输出:optional |
有值:直接返回原始optional;无值:调用函数获取兜底optional |
关键特点(为何用 Monadic 操作?)
- 消除显式条件 :无需写
if (opt.has_value()),链式调用自动处理分支,代码更简洁。例:原本需要判断course是否存在再拼接字符串,用transform+or_else可直接链式完成。 - 安全无空访问:仅当值存在时才执行转换/依赖逻辑,从根源避免"空指针解引用"风险。
- 类型严格匹配:每个操作的输入输出类型由模板强制约束,编译期即可发现类型错误。
实际场景示例(对应题目需求)
题目要求:用 2 个 Monadic 操作实现"有课程则输出详情,无则输出提示",核心逻辑如下:
cpp
std::optional<Course> course = db.find_course(argv[1]);
// 1. transform:有课程则拼接详情字符串(值转换),无则留空
// 2. or_else:无课程则返回兜底提示(兜底处理)
std::string output = course
.transform([](const Course& c) -> std::string {
return "Found course: " + c.title + "," + std::to_string(c.number_of_units) + "," + c.quarter;
})
.or_else([]() -> std::optional<std::string> {
return "Course not found."; // 无值时返回兜底字符串的 optional
})
.value(); // 此时必存在值,直接获取
std::cout << output << std::endl;
- 若
course有值:transform生成"课程详情字符串"的optional,or_else直接返回该optional,最终value()获取字符串。 - 若
course无值:transform返回nullopt,or_else触发兜底逻辑,返回"提示字符串"的optional,最终value()获取提示。