29

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

因此T * p, * q,一般来说,评估是非法的p < q

标准库包含函子类模板std::less<T>等,它们包装了内置的 operator <。但是,该标准对指针类型(20.8.5/8)有这样的说法:

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

这怎么可能实现?甚至有可能实现这一点吗?

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

4

5 回答 5

26

Can pointers be totally ordered? Not in portable, standard C++. That's why the standard requires the implementation to solve the problem, not you. For any given representation of a pointer, it should be possible to define an arbitrary total ordering, but how you do it will depend on the the representation of a pointer.

For machines with a flat address space and byte addressing, just treating the pointer as if it were a similarly sized integer or unsigned integer is usually enough; this is how most compilers will handle comparison within an object as well, so on such machines, there's no need for the library to specialize std::less et al. The "unspecified" behavior just happens to do the right thing.

For word addressed machines (and there is at least one still in production), it may be necessary to convert the pointers to void* before the compiler native comparison will work.

For machines with segmented architectures, more work may be necessary. It's typical on such machines to require an array to be entirely in one segment, and just compare the offset in the segment; this means that if a and b are two arbitrary pointers, you may end up with !(a < b) && !(b < a) but not a == b. In this case, the compiler must provide specializations of std::less<> et al for pointers, which (probably) extract the segment and the offset from the pointer, and do some sort of manipulation of them.

EDIT:

On other thing worth mentionning, perhaps: the guarantees in the C++ standard only apply to standard C++, or in this case, pointers obtained from standard C++. On most modern systems, it's rather easy to mmap the same file to two different address ranges, and have two pointers p and q which compare unequal, but which point to the same object.

于 2012-11-14T14:46:27.163 回答
12

是否可以在指针不形成全局总顺序的目标上实现标准库?

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

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

假设来自四个非空指针的任何两个不同指针对都不能与<. 我们可以简单地说,它std::less给了我们这个顺序:O ζ γ ψ χ,即使<没有。

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


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

于 2012-11-14T13:58:54.487 回答
5

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

在 GCC 和 Clang 的情况下,std::less只要<<. 由于他们是实现 的行为的人<,他们可以依赖此行为,但他们的用户不能,因为它可能会在未来发生变化。

于 2012-11-14T14:03:19.567 回答
5

问题在于分段架构,其中内存地址有两部分:段和偏移量。将这些部分转换为某种线性形式“很容易”,但这需要额外的代码,因此决定不为operator<. 对于分段架构,operator<可以简单地比较偏移量。早期版本的 Windows 存在此问题。

请注意,“足够简单”是系统程序员的观点。不同的段选择器可以引用同一个内存块,因此产生规范排序需要仔细检查段映射的细节,这取决于平台并且可能很慢。

于 2012-11-14T14:34:05.467 回答
1

我认为这个讨论中缺少一个更深层次的概念,那就是指针出处

原则上,您一般不能比较指针,但您应该能够比较来自同一指针的指针(通过算术运算)。例如,来自黑盒的指针(如对 的不同调用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* 没有算术)。


一些相关材料:http ://www.open-std.org/jtc1/sc22/wg14/www/docs/n2263.htm#pointer-provenance-in-c-and-provenance-within-allocated-regions

于 2020-04-14T02:20:21.683 回答