为什么C++ 11包含一个关于比较void指针的奇怪子句?

Mat*_*lia 58 c++ pointers comparison-operators language-lawyer c++11

在检查另一个问题的引用时,我注意到C++ 11中的一个奇数子句,在[expr.rel]3:

指针void(后指针转化)可以进行比较,以确定其结果如下:如果两个指针表示相同的地址或均为空指针值,其结果是true,如果操作者 <=>=false其他方式; 否则结果未指定.

这似乎意味着,一旦两个指针被投入void *,它们的排序关系就不再有保证; 例如,这个:

int foo[] = {1, 2, 3, 4, 5};
void *a = &foo[0];
void *b = &foo[1];
std::cout<<(a < b);
Run Code Online (Sandbox Code Playgroud)

似乎没有具体说明.

有趣的是,这个子句在C++ 03中不存在并且在C++ 14中消失了,所以如果我们采用上面的例子并应用C++ 14的措辞,我会说3.1

  • 如果两个指针指向同一数组的不同元素或其子对象,则指向具有较高下标的元素的指针比较大.

将应用,作为ab指向同一数组的元素,即使它们已被转换为void *.请注意,3.1的措辞在C++ 11中几乎完全相同,但似乎被该void *子句覆盖了.

我的理解是对的吗?在C++ 11中添加并立即删除的奇怪子句有什么意义?或者它可能仍然存在,但是被标准的其他部分移动/隐含?

P.W*_*P.W 36

TL; DR:

  • 在C++ 98/03中,该子句不存在,并且标准没有为void指针指定关系运算符(核心问题879,见本文末尾);
  • 关于比较void指针的奇怪条款是在C++ 11中添加来解决它的,但这又引发了另外两个核心问题583和1512(见下文);
  • 这些问题的解决要求删除该条款,并用C++ 14标准中的措辞代替,该标准允许"正常" void *比较.

核心问题583:关系指针与空指针常量的比较

  1. 与空指针常量的关系指针比较Section:8.9 [expr.rel]

在C中,这是不正确的(参见C99 6.5.8):

void f(char* s) {
    if (s < 0) { }
} ...but in C++, it's not. Why? Who would ever need to write (s > 0) when they could just as well write (s != 0)?
Run Code Online (Sandbox Code Playgroud)

这是自ARM以来的语言(可能更早); 显然这是因为只要其中一个操作数是指针类型,就需要在两个操作数上执行指针转换(7.11 [conv.ptr]).因此看起来"null-ptr-to-real-pointer-type"转换正在与其他指针转换搭便车.

拟议决议(2013年4月):

问题1512的决议解决了这个问题.

核心问题1512:指针比较与资格转换

  1. 指针比较与资格转换部分:8.9 [expr.rel]

根据8.9 [expr.rel]第2段,描述指针比较,

指针操作数(或指针操作数和空指针常量,或两个空指针常量,至少其中一个)执行指针转换(7.11 [conv.ptr])和限定转换(7.5 [conv.qual])是非整数的)将它们带到它们的复合指针类型.这似乎使以下示例格式错误,

 bool foo(int** x, const int** y) {
 return x < y;  // valid ?   } because int** cannot be converted to const int**, according to the rules of 7.5 [conv.qual] paragraph 4.
Run Code Online (Sandbox Code Playgroud)

对于指针比较来说这似乎太严格了,当前的实现接受了这个例子.

拟议决议(2012年11月):


解决上述问题的相关摘录见文章:指针比较与资格转换(修订版3).

以下还解决了核心问题583.

5.9 expr.rel第1至5段的变化:

在本节中,以下语句(C++ 11中奇数子句)已被删除:

指针void(后指针转化)可以进行比较,以确定其结果如下:如果两个指针表示相同的地址或均为空指针值,其结果是true,如果操作者<=>=false其他方式; 否则结果未指定

添加了以下声明:

  • 如果两个指针指向同一数组的不同元素或其子对象,则指向具有较高下标的元素的指针比较大.
  • 如果一个指针指向一个数组的元素或指向其子对象,而另一个指针指向一个超过该数组的最后一个元素的指针,则后一指针比较大.

因此,在C++ 14(n4140)部分[expr.rel]/3 的最终工作草案中,上述陈述是在解决时陈述的.


挖掘为什么添加这个奇数子句的原因让我得到了更早的问题879:缺少指针类型的内置比较运算符.该问题的拟议决议(2009年7月)导致增加了该条款,该条款于2009年10月被投票通过WP.

这就是它被包含在C++ 11标准中的方式.

  • 这解释了为什么它被删除了,但为什么它被添加到第一位呢?委员会试图通过限制"void*"的比较来实现什么?或者我错了,我的问题中的示例导致当前C++的未指定行为(IOW他们只是改变了措辞)? (3认同)
  • @MatteoItalia:我试图通过查找 C++11 标准中的“与 C++03 的兼容性”部分来寻找添加它的原因,但找不到任何相关的内容。当前的 C++(17) 也没有以任何形式提到这个子句(就我可以检查的标准而言),所以行为可能只是未指定。 (2认同)
  • "古怪条款"与两个核心问题无关.由于问题解决重写了子条款,因此它更像是一个驱动程序修复程序. (2认同)