在 C++ 里偷懒:让 value_or 的实参不再被提前求值
来源: cpp-rendering.io — 2026-09-12
概述
文章从一个反直觉的例子出发:std::optional a = 50 之后,a.value_or(complex_computation()) 竟然先打印出 Compute 才返回 50,而 C++ 的信条本来是「不用不为它付钱」。原因是 value_or 是函数,函数实参必须在调用前求值;C++23 的 or_else 接受可调用对象可以解决这个具体场景,但 std::map::try_emplace 等函数有同样的问题,需要一个通用解。作者先看 MSVC 的 value_or 实现,确认它只在 fallback 分支把参数转成 T,因此让计算发生在那个时点即可;随后给出一个极简的 Lazy 模板:内部存放一个 initializer 可调用对象,通过隐式转换运算符在真正需要物化结果时才调用它。局限性也明说:不记忆化,同一个 Lazy 用两次会算两遍;转换运算符没有约束,连 bool 都能转,可能出意外。下一篇将做支持记忆化、只转换到初始化器返回类型的更健壮版本,并实测运行时开销。
核心要点
- 起点是反直觉行为:函数实参先于调用求值,所以有值分支也会执行 complex_computation()。
- C++23 的 or_else 接受 callable,只解决这一类场景,try_emplace 等函数仍有同样问题。
- 关键洞察来自 MSVC 实现:value_or 只在 fallback 分支把参数转换成 T,所以把计算藏进转换里就够了。
- 解法是几行的 Lazy 模板:用隐式转换运算符包住 initializer,物化时才调用。
- 已知缺陷:不记忆化会重复计算,转换运算符无约束可能被隐式转成 bool。
- 下篇计划加上记忆化并限定转换目标类型,同时实测运行时开销。
金句
C++ 的信条是「不用不为它付钱」,可在这个例子里第一次计算根本没必要,我们照样付了钱。
👍 0
👎 0
返回 Lobsters 首页