为何为编译时常量字符串类实现operator<=>后仍需补充operator==?
关于C++中operator<=>与operator==的疑问
问题背景
我定义了一个代表编译时常量字符串的类Foo,它包含隐式转换为std::string_view的运算符,希望通过operator<=>实现与std::string_view、const char*、const std::string&等类字符串类型的比较。
初始代码
最初的代码仅支持Foo自身及与std::string_view的比较:
//! 代表字符串"foo"的单态类: struct Foo { constexpr operator std::string_view() const noexcept { return "foo"; } //! 支持与自身比较 friend constexpr auto operator<=>(const Foo&, const Foo&) = default; };
这符合预期,因为转换运算符可将Foo转为std::string_view进行比较。
添加模板化operator<=>后的情况
随后添加了模板化的operator<=>,试图支持与所有可和std::string_view比较的类型:
//! 支持与所有可和std::string_view比较的类型比较? friend constexpr auto operator<=>(const Foo& foo, const auto& other) { return static_cast<std::string_view>(foo) <=> other; }
此时可以实现与const char*、std::string的<、<=>双向比较,但无法使用operator==进行相等判断。
添加模板化operator==后的解决
当添加以下模板化的operator==后,所有比较均正常编译:
friend constexpr bool operator==(const auto& other, const Foo& self) { return other == static_cast<std::string_view>(self); }
核心疑问
为何需要额外添加operator==?我原以为operator<=>(const T&, const U&)支持这两种类型的双向相等比较,添加反向的operator<=>也无济于事,希望得到解释。
原因解释
这是C++20三路比较运算符的设计规则导致的:
operator<=>不自动生成反向相等判断:当你定义operator<=>(Foo, auto),它仅处理Foo作为左操作数的顺序比较(包括<、>、<=、>=以及<=>本身),但不会自动推导生成auto == Foo的相等判断重载。相等判断的重载匹配规则更严格,不会通过隐式转换或三路比较的反向推导来生成。- 相等判断的匹配优先级问题:当写
other == foo(比如std::string("foo") == Foo{})时,编译器会优先查找直接的operator==(const std::string&, const Foo&)重载。由于你的operator<=>是Foo作为左操作数的模板,无法反向适配右操作数的相等判断,且std::string的operator==也无法直接通过隐式转换匹配Foo(会因候选重载歧义或优先级问题失败)。 - 三路比较与相等判断分离:C++20中,
operator<=>负责顺序比较,operator==负责相等性判断,二者是独立的重载。即使定义了operator<=>,编译器也不会自动生成跨类型的operator==重载,尤其是涉及模板或隐式转换的场景。
简言之:operator<=>管「谁大谁小」,operator==管「是否相等」,跨类型的相等判断需要显式提供对应重载,无法依赖三路比较运算符自动推导。
内容的提问来源于stack exchange,提问作者Ben
相关产品推荐
相关产品推荐

