指针如何完全有序?

时间:2012-11-14 13:48:39

标签: c++ pointers predicate standard-library

C ++中的指针通常只能进行相等性比较。相比之下,只有两个指针指向同一个完整对象的子对象(例如数组元素),才允许小于比较。

所以给定T * p, * q,评估p < q一般是违法的。

标准库包含函数类模板std::less<T>等,它包含内置运算符<。但是,标准有关于指针类型(20.8.5 / 8)的说法:

  

对于模板greaterlessgreater_equalless_equal,任何指针类型的特化都会产生总顺序,即使内置运算符{{ 1}},<><=不会。

如何实现这一目标?甚至可以实现这个吗?

我看了GCC 4.7.2和Clang 3.2,它们根本没有包含任何指针类型的特化。他们似乎依赖>=无条件地在所有支持的平台上有效。

5 个答案:

答案 0 :(得分:16)

指针可以完全排序吗?不是便携式标准C ++。那是 为什么标准要求实施来解决问题,而不是 您。对于指针的任何给定表示,它应该是可能的 定义一个任意的总排序,但你如何做它将取决于 指针的表示。

对于具有扁平地址空间和字节寻址的机器,只需 将指针视为类似大小的整数或无符号整数 整数通常就足够了;这是大多数编译器将处理的方式 一个对象内部的比较,所以在这样的机器上,没有 需要图书馆专门化std::less等。 “未指明” 行为恰好是做正确的事。

对于单词寻址的机器(并且至少还有一个仍然存在 生产),可能有必要将指针转换为void* 在编译器本机比较之前可以工作。

对于具有分段架构的机器,可能需要做更多工作。 在这样的机器上通常要求阵列完全在一个中 段,只是比较段中的偏移量;这意味着如果 ab是两个任意指针,最终可能会!(a < b) && !(b < a)但不会a == b。在这种情况下,编译器必须提供 std::less<>等人的指针专业化(可能) 从指针中提取段和偏移量,然后进行某种操作 操纵它们。

编辑:

另外值得一提的是,或许:C ++中的保证 标准仅适用于标准C ++,或者在这种情况下,获得指针 来自标准C ++。在大多数现代系统中,mmap相当容易 相同的文件到两个不同的地址范围,并有两个指针pq比较不等,但指向同一个对象。

答案 1 :(得分:10)

  

是否可以在指针不构成全局总订单的目标上实现标准库?

是。给定任何有限集合,您总是可以在其上定义任意总顺序。

考虑一个简单的示例,其中您只有五个可能的不同指针值。我们称这些为O(对于nullptr),γ,ζ,χ,ψ 1

假设四个非空指针中没有两个不同的指针可以与<进行比较。我们可以简单地随意说std::less给我们这个顺序:Oζγψχ,即使<没有。

当然,以有效的方式实现这种任意排序是实施质量的问题。


1 我正在使用希腊字母来消除因熟悉拉丁字母而产生的潜意识命令概念;我向知道希腊字母顺序的读者致歉

答案 2 :(得分:4)

在大多数具有平坦地址空间的平台上,他们可以简单地在指针之间进行数值比较。在无法实现这一目标的平台上,实施者必须提出一些其他方法来建立要在std::less中使用的总订单,但他们可能会为<使用更有效的方法,因为它有一个较弱的保证。

对于GCC和Clang,他们可以std::less实施<,只要它们为<提供更强的保证。由于他们是实现<行为的人,他们可以依赖此行为,但他们的用户不能,因为它可能在将来发生变化。

答案 3 :(得分:3)

问题是分段体系结构,其中内存地址有两部分:段和偏移量。将这些部分转换为某种线性形式“足够简单”,但需要额外的代码,而且决定不对operator<强加开销。对于分段体系结构,operator<可以简单地比较偏移量。早期版本的Windows存在此问题。

请注意,“足够简单”是系统程序员的观点。不同的段选择器可以引用相同的内存块,因此生成规范排序需要仔细研究段映射的细节,这与平台相关并且可能很慢。

答案 4 :(得分:0)

我认为此讨论中缺少一个更深层次的概念,即指针出处

原则上,您通常不能比较指针,但是应该能够比较(通过算术运算)来自同一指针的指针。 例如,不能可靠地比较来自黑盒子的指针,例如对new的不同调用。 (这里的比较适用于排序,但我想严格来说,我不确定在这种情况下定义是否相等也是平等的。这将涵盖上面的mmap情况。)

因此,这是我的一个尝试(虽然没有用,但概念上):指针的比较是在顺序运算符适用性范围内的总顺序(即,在未定义时)。 从好的方面来说,是的,继续进行比较指针的比较,这些指针属于/来自同一分配或来自单个块。毕竟,如果p2 > p1

,它必须保持T* p2 = p1 + 1;

这与c ++中的容器迭代器类似,如果两个迭代器来自不同的容器,则没有必要进行比较。


编辑:Sean Parent https://youtu.be/mYrbivnruYw?t=3526对这个问题的看法。释义 (1)您只能比较相同容器的指针[我认为这太强大了,除了std::vector之外]。 (2)对指针使用std::less,因此它仅用于“表示”(例如,放入std::set)。 (3)一些编译器会抱怨比较(void?)指针。 (我认为这很好,因为void *没有算术)。