std::optional的value_or()不采用惰性求值的适用场景是什么
为什么
std::optional::value_or不采用惰性求值 std::optional::value_or不对参数做惰性求值是符合C++语言规则的设计,并非疏漏,核心原因可以归纳为三点:
- 首先是C++的函数调用基础规则限制:所有函数实参必须在进入函数体之前完成求值,不存在例外。
value_or只是std::optional的一个普通成员函数,签名为template<class U> T value_or(U&& default_value) const&,传入的bar()是普通实参,无论函数内部是否会用到这个参数,bar()都会在函数调用前先执行。这和if/else、&&、||这类语言内置的分支、短路逻辑有本质区别:后者是语言层面的控制结构,只有走到对应分支时才会执行分支内的表达式,普通函数根本没有权限改变实参的求值顺序和时机。 - 其次是接口通用性的权衡:如果要让
value_or支持惰性求值,要么把接口改成只接收可调用对象作为默认值参数,要么额外增加一套重载。前者会让90%以上的常见使用场景变得冗余——绝大多数时候开发者传的默认值都是字面量、已有变量,比如opt.value_or(0)、opt.value_or(default_val),如果强制要求传可调用对象,就必须写成opt.value_or([]{ return 0; }),平白增加无意义的样板代码;后者则会引入无法解决的歧义:如果optional存储的本身就是可调用对象类型(比如std::optional<std::function<int()>>),编译器根本无法判断传入的lambda是要作为默认存储的值,还是要执行后取返回值作为默认值,直接导致接口不可用。 - 最后是标准库的设计取舍:标准库工具优先为最高频的使用场景提供最简洁的写法,不会为了小众场景拉高通用场景的使用成本。真的需要惰性求值的场景,手动写分支判断的成本极低:
// 等价于惰性求值的value_or逻辑,没有任何额外开销 auto res = foo.has_value() ? *foo : bar();
完全不需要为了这类场景改动基础接口的设计。
很多开发者觉得这个设计反直觉,本质是混淆了普通函数参数传递和内置控制结构的语义差异,不是value_or特意拒绝惰性求值,是它作为普通函数从语言层面就做不到对普通参数做惰性求值。
内容的提问来源于stack exchange,提问作者m69 ''snarky and unwelcoming''
相关产品推荐
相关产品推荐

