题目
编写程序,演示 C++17 引入的核心实用工具类 std::optional<T>。告别传统 C 语言使用特殊魔法数字(如返回 -1、0 或 nullptr)代表查找失败的脆弱做法;设计一个安全的字典配置解析器,函数在命中时返回有效包装值,未命中时返回 std::nullopt;展示使用 has_value() 判定、value() 安全提取(空值时抛出 std::bad_optional_access)与 value_or(defaultVal) 提供保底默认值的现代优雅姿势。
输入格式:无需外部输入。
输出格式:输出有值读取、保底默认值降级与空值异常捕获测试。
数据范围与约定:C++17 std::optional、零堆开销值语义与空值类型安全。
思路与算法
1. 魔法数字的灾难
若查找温度函数返回 -1 表示失败,但 -1°C 恰好是一个完全合法的真实温度!使用 std::optional<double> 彻底将“是否成功”与“具体数值”解耦。
2. 零堆内存开销std::optional<T> 内部是 bool has_value_ 加上一块足够容纳 T 的未初始化栈内存对齐缓冲区(aligned_storage),绝对无需发生任何动态堆分配。
3. value_or 的防御魅力opt.value_or("default.conf") 可以在单行表达式内实现“有值则取值,无值则走安全降级默认值”,极大地精简了业务分支代码。
完整程序
#include <iostream>
#include <optional>
#include <string>
#include <unordered_map>
// 安全的配置项检索函数:返回 optional 包装器
std::optional<int> findServerPort(const std::string& serverName) {
static const std::unordered_map<std::string, int> registry = {
{"auth-service", 8081},
{"payment-gateway", 8082},
{"order-dispatcher", 8083}
};
auto it = registry.find(serverName);
if (it != registry.end()) {
return it->second; // 自动隐式包装为 std::optional<int>(it->second)
}
return std::nullopt; // 明确返回无值状态,杜绝魔法数字
}
int main() {
// 1. 成功命中的服务端口查询
auto port1 = findServerPort("auth-service");
if (port1.has_value()) {
std::cout << "auth-service 注册端口: " << port1.value() << "\n";
}
// 2. 未命中的服务端口查询:使用 value_or 优雅保底降级
auto port2 = findServerPort("unknown-service");
int actualPort = port2.value_or(80); // 无值时安全退化为默认 80 端口
std::cout << "unknown-service 解析端口 (带保底): " << actualPort
<< " (原始是否配置: " << std::boolalpha << port2.has_value() << ")\n\n";
// 3. 空值强取 value() 触发标准异常
std::cout << "--- 测试无值时调用 .value() 异常拦截 ---\n";
try {
int val = port2.value(); // 抛出异常
std::cout << "不可达输出: " << val << "\n";
} catch (const std::bad_optional_access& e) {
std::cout << "【安全捕获】拦截 std::bad_optional_access\n";
(void)e;
}
return 0;
}运行示例
输出:
auth-service 注册端口: 8081
unknown-service 解析端口 (带保底): 80 (原始是否配置: false)
--- 测试无值时调用 .value() 异常拦截 ---
【安全捕获】拦截 std::bad_optional_access