rub*_*ict 7 c++ arrays language-lawyer c++20
我一直在与飞船操作员一起玩,我想知道以下行为的原因是什么:
struct ArrayWrapper
{
int arr[3];
auto operator<=>(const ArrayWrapper&) const = default;
};
ArrayWrapper a1{1,2,3}, a2{1,2,4};
auto x = a1 <=> a2; // this compiles and works, x is std::strong_ordering::less
Run Code Online (Sandbox Code Playgroud)
数组比较适用于成员数组。它也适用于std::array:
std::array<int, 3> arr1{1,2,4};
std::array<int, 3> arr2{1,2,3};
auto x = arr1 <=> arr2; // x is std::strong_ordering::greater
Run Code Online (Sandbox Code Playgroud)
但是,它不适用于非成员的原始数组:
int rawArr1[3]{1,2,3}, rawArr2[3]{1,2,3};
auto x = rawArr1 <=> rawArr2; // error: invalid operands of types ‘int [3]’ and ‘int [3]’ to binary ‘operator<=>’
Run Code Online (Sandbox Code Playgroud)
我已经在 GCC 11 上测试过了,原因是什么?考虑到它适用于数组成员,这似乎很奇怪。
三路比较的目标是使(新引入的)隐式生成的比较与隐式生成的复制语义(预先存在;不考虑弃用情况)一致。复制时可以观察到一致的现象:
ArrayWrapper a3 = a2; // OK
int rawArr3[] = rawArr2; // ill-formed
Run Code Online (Sandbox Code Playgroud)
最初的提案是这样说的:
P0515R0 一致比较
2.2.3 语言类型和
operator<=>
- 对于可复制数组
T[N](即,非静态数据成员),T[N] <=> T[N]返回与T's相同的类型<=>并执行按字典顺序的元素比较。对于其他数组,没有,<=>因为数组不可复制。注释 ... 对于数组,如果数组在语言中不可复制,我们不提供比较,以保持复制和比较的一致性。请注意,对于两个数组, arr1<=>arr2 格式错误,因为未应用数组到指针的转换。
以及规则的措辞(最新草案):
[expr.spaceship]
- 如果至少一个操作数是对象指针类型而另一个操作数是对象指针或数组类型,则数组到指针转换 ([conv.array])、指针转换 ([conv.ptr]) 和限定对两个操作数执行转换以将它们转换为复合指针类型 ([expr.type])。转换后,操作数应具有相同的类型。[注 1:如果两个操作数都是数组,则不应用数组到指针的转换。— 尾注]
...没有其他情况适用于数组
- 否则,程序格式错误。
这就是为什么rawArr1 <=> rawArr2不起作用。
[class.compare.default]
C 的直接基类子对象,按照它们在 C 的基本说明符列表中的声明顺序,然后是 C 的非静态数据成员,按照它们在 C 的成员说明中的声明顺序,形式子对象列表。在该列表中,任何数组类型的子对象都按照下标递增的顺序递归扩展为其元素的序列。设 xi 是一个左值,表示对象 x(长度为 n)的子对象扩展列表中的第 i 个元素,其中 xi 由一系列派生到基类转换([over.best.ics])形成,类成员访问表达式 ([expr.ref]) 和应用于 x 的数组下标表达式 ([expr.sub])。
这就是为什么a1 <=> a2有效。
此外,保证std::array:
[container.requirements.general]
Run Code Online (Sandbox Code Playgroud)i <=> j约束条件: X?::?iterator 满足随机访问迭代器的要求。