带const限定的重载决议疑问:为何编译器选非const转换运算符?
C++隐式转换序列的优先级解析
先看示例代码:
#include <iostream> struct Foo {}; struct Bar : Foo {}; struct Baz : Foo {}; struct X { operator Bar() { std::cout << "Bar\n"; return {}; } operator Baz() const { std::cout << "Baz\n"; return {}; } }; void foo(const Foo &f) {} int main() { foo(X{}); }
GCC与Clang编译运行后均输出"Bar"。
用户的疑问点在于:右值对象可以调用const修饰的成员方法,理论上存在两个可行的隐式转换序列:
X{} -> 用户转换(operator Bar()) -> Bar -> const Foo&X{} -> 用户转换(operator Baz() const) -> Baz -> const Foo&
两个序列都包含1次用户转换和等价的标准转换尾,按C++20标准的[over.ics.rank]条款似乎应该存在歧义,但主流编译器都选择了非const的转换函数,原因如下:
关键规则:用户转换函数隐含对象参数的比较逻辑
在重载决议比较用户定义转换序列时,除了转换到目标类型的序列,还需要比较绑定到转换函数隐含对象参数(即this指针)的转换序列优劣,C++标准对此有特殊规定:
- 当比较同一个类的成员转换函数时,非const成员函数的隐含对象参数会被当作类类型的左值处理;
- const成员函数的隐含对象参数会被当作类类型的右值处理。
结合示例分析:
- 对于
operator Bar()(非const成员):
隐含对象参数为X&,按照规则,源表达式X{}被当作左值处理,绑定到X&属于精确匹配(无任何转换),转换序列等级最高。 - 对于
operator Baz() const(const成员):
隐含对象参数为const X&,源表达式X{}被当作右值处理,绑定到const X&需要添加const限定符,属于限定转换,转换序列等级低于精确匹配。
根据[over.ics.rank]的排序规则,精确匹配的转换序列优于限定转换,因此编译器会优先选择operator Bar(),最终输出"Bar"。
内容的提问来源于stack exchange,提问作者Konstantin Vladimirov
相关产品推荐
相关产品推荐

