【问题标题】:Order of items in STL SET differ for different runs of programSTL SET中的项目顺序因程序的不同运行而异
【发布时间】:2014-05-26 12:46:34
【问题描述】:

我正在使用 stl set 容器类来存储指向对象的指针。在从 stl 集合中读取时,对于程序的不同运行,由于内存(地址)动态分配给对象,对象的顺序会发生变化。

假设对象是 A、B、C,它们在第一次运行时的地址是 10、16、12。因此,当我将这些对象插入 set 并检索它们时,我将得到输出为 A C B (b'caz 10

有什么方法可以让我按特定顺序获得输出?

重载比较器(自定义比较器)可以解决问题,但是这种情况下我必须将它作为模板参数传递,这会导致在很多地方修改代码。有什么方法可以避免修改我的代码,仍然能够编写客户比较器?

【问题讨论】:

  • 避免使用指针,将对象存储在容器中(不是指针),不要解决实际问题。
  • 为什么要在容器中存储指针?存储对象而不是指向对象的指针是非常安全的。事实上,存储指针(而不是 std::unique_ptrs 或 std::shared_ptrs)可能会导致内存泄漏,而存储对象则不会,因此它比您所做的更安全。
  • @MaxLybbert :是的,我理解存储指针的问题,但我只能根据给定的需要存储指针。

标签: c++ stl set


【解决方案1】:

您可能应该传递一个按指向的值排序的比较对象,而不是按指针的值排序:

struct deref_compare {
    template <typename T>
    bool operator()(T const* o0, T const* o1) const {
        return *o0 < *o1;
    }
};

std::set<int*, deref_compare> s;
// use s as before

如果您无法更改 std::set&lt;int*&gt; 的声明,您可以技术上专门化 std::less&lt;int*&gt; 来做一些不同的事情,但除非您专门涉及用户定义的类型,否则这是不允许的:

namespace std
{
    template <>
    class less<int*> {
    public:
        bool operator()(int const* o0, int const* o1) const {
            return *o0 < *o1;
        }
    };
}

请注意,在不涉及用户定义类型的情况下专门化任何标准类模板会导致未定义的行为:我建议不要专门化 std::less&lt;int*&gt;。问题是它会影响其他代码,例如标准 C++ 库中的代码。 C++ 标准的相关部分是 17.6.4.2.1 [namespace.std] 第 1 段:

除非另有说明,否则如果 C++ 程序将声明或定义添加到命名空间 std 或命名空间 std 内的命名空间,则其行为未定义。只有当声明依赖于用户定义的类型并且特化满足原始模板的标准库要求并且没有明确禁止时,程序才能将任何标准库模板的模板特化添加到命名空间std

【讨论】:

  • 在这种情况下,我必须将 deref_compare 作为模板参数传递,这会导致在我的代码中的许多地方进行修改。有什么办法可以避免修改代码? ?
  • @Vinay:如果专业化涉及至少一种用户定义的类型,则允许您专门化标准模板。例如,如果您定义了struct MyType {};,您可以专门化std::less&lt;MyType&gt;std::less&lt;int*&gt; 不涉及用户定义的类型,也就是说,尽管您可以在技术上对其进行专门化(即编译器可能不会阻止您),但结果是未定义的。
  • 如果代码不符合您的要求,您将不得不对其进行修改。当然,问题是更改的范围有多大,但就其价值而言,我没有看到需要更改更少代码行的解决方案。
  • 基本上,专业化可能有助于快速解决问题。这不是一个合适的解决方案,要创建一个合适的解决方案,您需要修复您的代码。请注意,正确的修复几乎肯定还涉及存储正确的、不可变的值:更改任何指向 ints 的值都可能会破坏集合的不变量。
【解决方案2】:

由于std::set 中的顺序是通过元素的自然排序来管理的,因此如果您需要特定的顺序,则需要一个不会改变每次特定运行的特定标准。这是因为指针的自然排序只涉及地址本身的值。

这意味着您需要有一种方法可以通过特定的比较运算符对它们给出一致的顺序,例如:

struct my_order {
    bool operator() (const Object* lhs, const Object* rhs) const {
      // return consistent value for same pair of lhs and rhs
    }

set<Object*, my_order> mySet;

【讨论】:

  • 在这种情况下,我必须将 my_order 作为模板参数传递,这会导致在我的代码中的许多地方进行修改。有什么办法可以避免修改代码? ?
  • @Vinay:您应该只需要更改定义集合类型的一个 typedef :-)
【解决方案3】:

您可以指定一个比较函数。

在我写这篇文章时,问题中没有代码,因此无法使用您的类等提供示例。

只需查看documentation

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-22
    • 2013-08-13
    • 1970-01-01
    • 2020-01-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多