【问题标题】:Does a program with std::map<T*, U> have well-defined behaviour? [duplicate]具有 std::map<T*, U> 的程序是否具有明确定义的行为? [复制]
【发布时间】:2020-04-23 11:09:06
【问题描述】:

Comparing pointers to unrelated objects has unspecified results.

这似乎表明该程序可能具有未定义的行为,至少因为我们不能保证键类型的严格弱排序:

#include <map>

int main()
{
    int x = 0, y = 1;
    bool arbitrary = false;

    std::map<int*, bool> m{
       {&x, arbitrary},
       {&y, arbitrary}
    };
}

那么,更一般地说,我们可以说带有指针键的地图是一个危险的*命题吗?或者我们可以在这里依靠一些特殊的规则?

* 从学术上讲,就是;实际上,我不知道主流实现实际上会在比较任意指针时引发地狱。

【问题讨论】:

标签: c++ sorting pointers language-lawyer strict-weak-ordering


【解决方案1】:

也就是说;实际上,我不知道主流实现 实际上会在比较任意指针时引发地狱。

取决于 地狱 的含义。地狱不仅仅是立即崩溃或用随机数据扰乱内存。它可能会给应该是确定性函数的结果带来不确定的结果。

众所周知,GCC 可以优化指向不同完整对象 A 和 B 的相关(虚拟)(子)对象的对象的指针比较:基于 &amp;A 的任何内容都会与基于 &amp;B 的任何内容进行比较,甚至&amp;A+1,即使地址实际上相同。我不知道这是否在 C 或 C++ 中观察到,但您的问题几乎是 C/C++(也适用于 qsort)。

也就是说,指针比较的结果将取决于指针的已知来源。这意味着根据优化级别、内联上下文、翻译单元等,比较可能会得出不同的结果。

所以是的,如果您进行这些指针比较,它至少可以在流行编译器的某些版本上崩溃。

std::map 不是在术语 operator&lt; 中定义的,而是在 std::less 术语中定义的。

【讨论】:

  • 授予;当我认为指针比较本身可能有 UB 时,我想“提高地狱”是问题的早期版本中留下的措辞;损坏的订单仍然可以,但这不是引用位所说的。我也应该更新它。但如果我有,我就不会收到这个有用的补充:)
【解决方案2】:

std::map 使用 std::less 专门针对指针类型:

任何指针类型的 std::less 特化都会产生一个严格的 总顺序,即使内置 operator、=)。

对于更具体的描述,我给你留下 2 个链接:

std::less first link

std::less second link

【讨论】:

    【解决方案3】:

    std::lessstd::map 的默认比较器)对指针有特殊处理,允许:

    std::less 对任何指针类型的特殊化都会产生严格的总顺序,即使内置的 operator&lt; 没有。

    关于

    我们可以说带有指针键的地图是一个危险的命题吗?

    所以总的来说还不错。

    应使用const char* 键采取额外的预防措施:

    我们比较指针而不是字符串内容(主要是初学者的困惑)。

    具有相同内容的 C 字符串文字不保证相等:

    "literal" == "literal"; // Not guaranteed
    "literal" < "literal"; // false .. or true
    

    【讨论】:

    • OTOH 在保证静态变量存在于程序的一个实例中的范围内,保证文字字符串的每个源代码实例都有一个明确定义的地址,因此inline void *literal() { return "literal";} 保证literal()==literal()。 OTOH diff TU 中的 diff 文件静态函数中的相同文字字符串没有这样的保证。
    【解决方案4】:

    是的,因为std::map 默认比较运算符是std::less,与标准比较运算符不同,它完全是为指针类型定义的。

    [comparisons#2]

    对于less、greater、less_equal和greater_equal模板,任何指针类型的特化产生的结果与实现定义的严格指针总顺序一致([defns.order.ptr])。

    [defns.order.ptr] 中将实现定义的严格的指针总顺序定义为:

    实现定义的对所有指针值的严格总排序,使得排序与内置运算符 、= 和 施加的偏序一致

    【讨论】:

    • 哦,是的,哈哈。不错的一个
    • 有意思,直接违反[expr.rel]/4吧?
    • @rustyx 据我了解,该标准强制实现对所有指针具有严格的总顺序,但该顺序不是通过内置运算符呈现,而是仅通过std::less-like 仿函数呈现。因此有两种不同的“顺序”(一种特定于实现的可通过std::less 使用,另一种用于内置运算符),并且在定义两者时它们必须保持一致(相同的数组/相同对象的成员)。
    • 但它说“内置运算符强加的顺序”;)
    • @rustyx "实现-定义的指针上的严格全序"必须与标准定义的partial一致> 内置运算符强加的顺序。
    猜你喜欢
    • 1970-01-01
    • 2022-01-09
    • 1970-01-01
    • 2018-06-20
    • 2013-09-10
    • 2011-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多