【问题标题】:Why is it possible to compare array members using operator<=>, but not freestanding arrays?为什么可以使用 operator<=> 比较数组成员,而不是独立数组?
【发布时间】:2021-09-26 13:32:30
【问题描述】:

我一直在和宇宙飞船操作员一起玩,我想知道以下行为的原因是什么:

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

数组比较适用于成员数组。它也适用于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

但它不适用于非成员的原始数组:

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<=>’

我已经在 GCC 11 上测试过了。这是什么原因?考虑到它适用于数组成员,这似乎很奇怪。

【问题讨论】:

  • 好问题(在我改写了它的标题之后......:-P);虽然是 IIANM,但应该直接为数组实现模板化 operator&lt;=&gt;() - 只需通过引用获取它们,而不是让它们衰减。
  • 你的ArrayWrapperstd::array 真的没有太大区别。 C++ 中的普通数组的问题在于,它们实际上是一种单独的类型,并且是一种非常特殊的类型,它没有为它定义所有类型的操作,只是一个小而有限的集合。
  • auto x = rawArr1 &lt; rawArr2; 由于指针强制,已经意味着某些东西,因此基于值的宇宙飞船运算符可能会令人困惑和/或存在向后兼容性问题。
  • 我认为可以肯定地说现在不鼓励使用普通的原始数组。它们不会使用新功能进行改造。
  • 数组不是一等公民。有很多事情他们做不到,而更健壮的类型可以做到。

标签: c++ arrays language-lawyer c++20


【解决方案1】:

三路比较的一个目标是使(新引入的)隐式生成的比较与隐式生成的复制语义(预先存在的;不考虑弃用的情况)一致。通过复制可以观察到一致的现象:

ArrayWrapper a3          = a2;      // OK
int          rawArr3[]   = rawArr2; // ill-formed

原来的提案是这样说的:

P0515R0 一致比较

2.2.3 语言类型和operator&lt;=&gt;

  • 对于可复制数组T[N](即,它们是非静态数据成员),T[N] &lt;=&gt; T[N] 返回与T&lt;=&gt; 相同的类型,并执行字典元素比较。对于其他数组,没有&lt;=&gt;,因为数组不可复制。

注意...对于数组,如果数组在语言中不可复制,我们不提供比较,以保持复制和比较的一致。请注意,对于两个数组,arr1arr2 格式错误,因为未应用数组到指针的转换。

以及规则的措辞(最新草案):

[expr.spaceship]

  • 如果至少一个操作数是对象指针类型,而另一个操作数是对象指针或数组类型,则数组到指针转换 ([conv.array])、指针转换 ([conv.ptr]) , 并对两个操作数执行限定转换,以将它们转换为复合指针类型 ([expr.type])。 转换后,操作数应具有相同的类型。 [注 1:如果两个操作数都是数组,则不应用数组到指针的转换。 ——尾注]

...没有其他适用于数组的情况

  • 否则,程序格式错误。

这就是rawArr1 &lt;=&gt; rawArr2 不起作用的原因。

[class.compare.default]

C 的直接基类子对象,按照它们在 C 的基本说明符列表中的声明顺序,然后是 C 的非静态数据成员,按照它们在成员规范中的声明顺序C、形成子对象列表。 在该列表中,任何数组类型的子对象都递归地扩展为其元素的序列,按下标递增的顺序。 设 xi 是一个左值,表示对象 x(长度为 n)的子对象扩展列表中的第 i 个元素,其中 xi 由一系列派生到基的转换([over.best.ics])形成,类成员访问表达式 ([expr.ref]) 和数组下标表达式 ([expr.sub]) 应用于 x。

这就是a1 &lt;=&gt; a2 起作用的原因。

另外,保证std::array

[container.requirements.general]

i <=> j

约束:X​::​iterator 满足随机访问迭代器要求。

【讨论】:

    猜你喜欢
    • 2021-01-16
    • 1970-01-01
    • 1970-01-01
    • 2019-02-14
    • 1970-01-01
    • 2021-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多