为何std::optional与自定义Optional类的转换运算符选择不同?
差异原因解析
1. std::optional场景的选择逻辑
std::optional<double>没有直接接受double的非模板构造/赋值函数,它用来构造带值实例的是一个模板函数:
template <class U = T> constexpr optional(U&& value);
当将Foo赋值给std::optional<double>时,有两个候选路径:
- 路径A:
Foo通过operator double()转换为double,再匹配上述模板构造/赋值函数(此时U=double) - 路径B:
Foo通过operator std::optional<double>()转换为std::optional<double>,再匹配非模板的移动构造/赋值函数
根据C++重载解析规则,两个路径的用户定义转换次数相同,但std::optional的模板构造/赋值函数设计意图是优先从值类型构造实例(而非从另一个optional构造,除非源类型本身就是optional)。编译器会倾向于选择更贴合这个设计的路径A,因此调用Foo的double转换运算符。
2. 自定义Optional场景的选择逻辑
自定义的Optional<double>拥有非模板的构造函数:
Optional(double v);
同时Foo的operator Optional<double>()是直接转换为目标类型的运算符。此时候选路径为:
- 路径A:
Foo→double→匹配非模板构造/赋值函数 - 路径B:
Foo→Optional<double>→匹配非模板移动构造/赋值函数
根据C++重载解析规则,直接转换到目标类型的序列优先级更高——路径B仅需一次用户定义转换就得到与目标完全匹配的类型,无需额外构造步骤,因此编译器选择路径B,调用Foo的Optional<double>转换运算符。
内容的提问来源于stack exchange,提问作者AndyMcoy
相关产品推荐
相关产品推荐

