我尝试在包含如下引用成员的简单类中将operator==
和默认为:operator<=>
#include <iostream>
#include <string>
class Simple
{
public:
Simple(const std::string& data)
: data_(data)
{
}
auto operator<=>(const Simple&) const = default;
private:
const std::string& data_;
};
int main(int argc, char** argv)
{
std::string str1 = "one";
Simple s1(str1);
std::string str2 = "two";
Simple s2(str2);
std::cout << (s1 < s2) << std::endl; // compiler error
return 0;
}
clang 编译器指出
warning: explicitly defaulted three-way comparison operator is implicitly deleted
note: defaulted 'operator<=>' is implicitly deleted because class 'Simple' has a reference member
我没有收到其他编译器(例如 MSVC)的警告,但是当我尝试使用它时,出现编译错误:
<source>(62): error C2280: 'auto Simple::operator <=>(const Simple &) const': attempting to reference a deleted function
<source>(49): note: see declaration of 'Simple::operator <=>'
<source>(49): note: 'auto Simple::operator <=>(const Simple &) const': function was implicitly deleted because 'Simple' data member 'Simple::data_' of type 'const std::string &' is a reference type
<source>(52): note: see declaration of 'Simple::data_'
其他默认功能(如复制分配)当然也会被删除,因为它们无法通过引用成员实现。
但为什么引用指向的内容不能自动进行比较呢?
并且手动实现它的最短方法是什么?
论文P1603R1规定引用成员应该导致删除比较运算符,并表示这符合论文 P0515:
论文继续说道(这次是我的重点):
也就是说,做出了最安全的选择,并且如果有足够多的动机来改变规则,那么就可以进行改变。
引用成员确实阻止编译器生成默认的比较运算符,因为您可以比较引用所指的内容或地址。
您可以
operator<=>
通过以下方式手动实现:Godbolt 演示。
您可以类似地实施
operator==
检查相等性的方法。这里的问题是,在具有引用成员的类中
operator<=>
,和operator==
不能默认,因为默认实现依赖于直接比较所有数据成员。但是,引用不支持复制赋值或移动操作,因此当涉及引用时,编译器无法合成这些运算符。当编译器尝试将 设为默认值时
operator<=>
,它会尝试单独比较每个成员。但由于data_
是引用,它没有自己的值 — 它仅指向另一个对象,因此编译器无法像对值类型那样执行直接比较。这就是比较运算符被隐式删除的原因。您需要手动实现
operator==
并operator<=>
比较引用指向的对象,而不是引用本身。具体操作如下:operator==
:我们手动定义operator==
比较并data_
通过比较它们引用的对象来比较。s1
s2
operator<=>
:同样,我们手动定义在 中进行operator<=>
比较,并使用。由于是引用,因此在 引用指向的对象之间进行比较。data_
s1
s2
operator<=>
std::string
data_
data_ <=> other.data_
std::string
由于引用本身不是值,因此需要通过它们引用的对象进行比较。此手动实现解决了编译器无法默认
operator==
以及operator<=>
具有引用成员的类型的问题。这是在不改变类设计的情况下处理这种情况的最短、最干净的方法。