【问题标题】:Universal less<> for pointers in C++ standard通用 less<> 用于 C++ 标准中的指针
【发布时间】:2009-07-08 15:52:53
【问题描述】:

很多时候我需要一组指针。每次发生这种情况时,我都会为指针类型编写一个 less 实现 - 将两个指针强制转换为 size_t 并比较结果。

我的问题是 - 在标准中是否可用?我找不到类似的东西。似乎很常见的情况......

更新:似乎即将发布的标准修复了为指针类型和 unordered_set 提供的 less 的所有问题。几年后,这个问题将没有实际意义。

与此同时,当前标准对此没有“合法”解决方案,但 size_t cast 有效。

更新更新:好吧,我会大吃一惊!不仅

std::map<void *, int, std::less<void*> > myMap;

有效,但甚至

std::map<void *, int > myMap;

也是。

这是在 gcc 3.4.1 中。我一直在做所有这些演员,而 litb 是完全正确的。甚至他引用的章节编号在当前标准中也完全相同。万岁!

【问题讨论】:

  • “同时,当前标准对此没有“合法”解决方案,但 size_t cast 有效。” less 解决方案当前标准之一。它不是特定于 C++0x 的

标签: c++


【解决方案1】:

可以使用比较函数对象lessgreater 等来比较两个指针。否则,使用毯子operator&lt; 等,只有当指针指向同一个数组对象的元素或一个过去的元素时才有可能结束。否则,结果未指定。

C++03 中的20.3.3/8

对于模板 greaterlessgreater_equalless_equal,任何 指针类型产生一个总顺序,即使内置运算符&lt;&gt;&lt;=&gt;= 没有。

无需显式专门化并手动转换为size_t:这甚至会降低可移植性,因为reinterpret_cast 从指针到整数的映射是实现定义的,不需要产生任何顺序。


编辑:如需更详细的答案,请参阅this one

【讨论】:

  • Neil,在我看来,标准确实在“任何指针类型的特化产生一个总顺序”时特别说明了这一点。如果您仍然有未定义的行为,则不能有总订单,因此该语句必须从模板中删除未定义性。
  • 万一有人算计,我又投了一票“尼尔误读了规范”。总订单是指整个类型的总订单。仅包含某些值的“总订单”在技术上称为“部分订单”。我推测与 C 不同的原因正是 std::set 如果 int* 不可比较则无法实现。因此,该标准规定 int* 具有可比性。我不明白的是为什么标准不只是说 operator
  • 顺便说一句,我可以建议更多人投票支持原始问题吗?事实证明这是一个很好的问题!
  • @Neil 查看19.5.1.3/3 的最新草案,该草案将operator&lt; 定义为error_categorystd::less&lt;stuff&gt;()(this, &amp;rhs) 方面,并带有注释"less 提供了总排序指针”.
  • @Neil:“架构可能不支持可比较的指针”。我不明白这有多重要。该标准并未规定 std::less 必须快!它只要求它提供一个排序。如果架构不支持它,那么编译器/库需要提供某种替代机制来获取排序。例如,将地址映射到订单值的查找表。仅仅因为架构不支持它并不意味着它不能完成。
【解决方案2】:

不,它不可用。 标准规定,只有当指针指向同一个数组或其他内存块时,指针才可与内置运算符进行比较。也就是说,块需要一次全部分配,而不是可能碰巧彼此相邻的两个单独分配。

没关系:

int x[2];
bool b = &x[0] < &x[1];

这是未定义的:

int x0;
int x1;
bool b = &x0 < &x1;

没关系:

struct foo {
  int x0;
  int x1;
};
foo f;
bool b = &f.x0 < &f.x1;

两个值都是同一个结构的成员,所以它们属于同一个内存块(即f's)。

不过,实际上,您的自定义比较并没有错。

但是,您的自定义特化是不必要的,因为std::less 模板 显然是为指针定义的。所以这没关系:

int x0;
int x1;
std::less<int*> compare;
bool b = compare(&x0, &x1);

仍然没有任何迹象表明结果必须是什么,但至少承诺您会有 一些 结果,而不是未定义的行为。您得到一个总订单,但在运行它之前您不知道什么订单。

【讨论】:

  • 这是不正确的,可以得到指针的相对排序。 std::less (根据 litb 的回答)保证返回正确的顺序。
  • @Richard Corden - 它可能会返回 an 排序,但没有什么“正确”的,因为对于不在单个块中的位置的指针没有有意义的排序或数组。
  • @Earwicker:我不确定你在这里暗示什么。无论如何,说两个指针都小于对方是什么意思!关键是 std::less 由 C++ 委员会定义,为指针类型提供一致的顺序。如果您需要 Comparision 对象用于要为其存储唯一指针的映射/集,那么 std::less 就是您的人选。关于“正确”,我指的是评论:“标准说指针只有在指向同一个数组或其他内存块时才具有可比性”。这不是真的,std::less 比较所有指针。
  • 我真的只是在狡辩-我认为您的“正确”是指某种通用或可移植标准可能认为有意义或有用的排序,当(正如您在下一条评论中所说)排序由实现选择。足以存储唯一指针,但如果您希望它们在不同系统上以相同的顺序迭代,那就不好了。
  • @Earwicker:告诉他们,当他们在邮局分拣信封时,他们会根据房子的地址,而不是住在那里的人的姓氏来分拣;-)
【解决方案3】:

比较指针是一件相当麻烦的事情。只有当它们都指向同一个内存块时,比较指针才有意义,否则操作是未定义的。因此,一组(有效的)指针是非常专业的东西,不,标准库没有。

【讨论】:

  • litb 是正确的(再次在这里)。 'std::less' 等。保证给出一个明确的顺序。再次直接解决您对 litb 的回答所做的评论,如果它们没有提供额外的东西,委员会为什么还要明确区分这些功能与内置运算符?
  • @Martin 因为 C++ 和 C 没有指定指针内的位模式是什么意思。
  • 该标准试图将其保持打开状态,以便指针的定义比这更抽象。它是否成功是另一回事,但理论上如果比较两个指针并且它们不是来自同一个块,则允许实现格式化您的硬盘驱动器等(在此处插入最喜欢的“未定义行为”陈词滥调)。
  • 呸,那是@Martin,但他消失了。
【解决方案4】:

尽管您可能认为比较指针的位是无害的(毕竟,它们只是内存中的地址,对吧?),但语言标准不鼓励这样做是有充分理由的。一方面,如果您的代码的结果取决于指针的相对顺序(例如,结果的顺序取决于通过一组或指针映射的迭代顺序),结果将不稳定:更改编译器或操作系统的版本发布可以改变结果。可重复性的价值足以让这种不稳定性值得避免。

【讨论】:

    【解决方案5】:

    比较未分配在同一块中的指针是未定义的,可能是因为存在内存模型存在问题(并且过去更常见)。

    您需要使用的是unordered_set&lt;&gt;,它似乎不需要比较运算符。它在下一个 C++ 标准中,同时可用 as a Boost header

    【讨论】:

    • +1 另见 Dmitry Risenberg 的回答,它为无序容器使用了错误的名称,但在其他方面也是如此。
    【解决方案6】:

    hash_set 不是你需要的吗?它将成为下一个标准的一部分

    【讨论】:

    • 容器不是问题。问题是容器的比较运算符 used 。没有为任意指针定义这样的运算符。
    • 首先,它是下一个标准和 Boost 库中的 unordered_set。其次,容器是问题。使用标准集,您需要比较运算符,这些运算符未为指针定义。使用 unordered_set,你不会(据我所知)。
    • +1 - 容器是问题所在:set 是一个有序容器,因此它不适合指针,一般来说它们不是有序的。
    • 我明白了。 hash_set 和 unordered_set 类使用哈希函数转换指针,因此指针不再相互比较。它们通过它们的哈希值进行比较,并比较它们是否相等。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-30
    • 1970-01-01
    • 2021-08-25
    • 1970-01-01
    • 1970-01-01
    • 2021-03-24
    相关资源
    最近更新 更多