有没有办法将 references/pointers 保留到 unordered_set 中的对象?

Is there a way to keep references/pointers to objects in unordered_set?

我有几个 class A 的对象。在其他一些 class B 中,我想保留 unordered_set 个 class A 的对象。请注意,class B 的对象因此包含一个集合,该集合可能指向(某些)与另一个集合相同的 class A 对象。要求永远不要复制这些 class A 对象或任何东西,因为它们包含应该由 class B 和 [=13] 的不同对象使用和更改(使用中共享)的成员数据=].

首先,由于我来自 C,我想只使用指向对象的指针作为集合中的值。然而,在编码时我发现我现在还需要传递覆盖的哈希和相等功能,因为我必须比较 class A 的对象是否与 class A 的私有成员相等。因此,哈希函数需要是 class A.

的 public 成员函数

不过我想知道,因为 std::unordered_set::insert() 似乎采用 引用 value_type,是否可以只使用 A 作为值键入 std::unordered_set。这将使代码更容易一些,因为它默认使用 class A 中已经实现的 == operator。但是,我不确定 std::unordered_set::insert() 是否复制了您传递给它的对象。似乎没有,但是 value_type 仍然是 A,而不是 A&。

我也看不出std::unordered_set::insert()std::unordered_set::emplace()的区别。我不明白 "copy vs move" 是什么意思。

TLDR:要在集合或映射中保留对用户定义的 classes 对象的引用,在这种情况下是否可以将 A* 用作 value_type?还是std::reference_wrapper<A>?或者只是普通的 A?

这是一个最小的可重现示例:

class A {
public:
    bool operator== (A& other);
private:
    B* b;
    size_t id;
    size_t someIntThatMustBeShared;
};

class B {
public:
    std::unordered_set<A ? > as;
};

免责声明:我是 OP,我是 C++ 新手。我的代码现在按预期编译和运行,虽然我相信以下所有信息都是正确的,但请发表评论以通知我任何错误。

对于那些感兴趣的人,我最终在 class A 中创建了两个 public 仿函数(内部,因为我需要访问私有成员(使用 friend 也将是一个option)) 并在 std::unordered_set 的声明中使用它们。对于我使用 A* 的类型。更好的解决方案可能是智能指针,但我对这一切都是新手,所以我还没有详细研究这些。

我了解到以下关于 std::unordered_set 的事情:

  1. 默认的相等和散列行为仅针对列出的类型实现 here
  2. 如果您需要其他任何东西,您有 3 个选择:使用某种指向类型的指针(是原始的,std::shared_ptrstd::unique_ptr、...),使用 std::reference_wrapper<T>,或使用类型本身。请注意,对于 std::unordered_set,不允许像 A& 这样的引用作为 value_type,因此是包装器。
  3. 在前两个选项中,默认实现(如果有)不会'cascade down'使用您可能为您的类型定义的任何operator ==。在第 5 点中提出了解决方案。
  4. 在最后一个选项中,您的类型 operator == 被用作默认值 'equaller'。在这里工作只需要一个散列器的定义。但是,您应该小心 std::unordered_set::insert() 创建插入元素的副本,因此如果它包含静态分配的成员数据,您应该确保在开始复制该类型的实例时您的程序仍然正常运行。我认为这是最不冗长的解决方案,也是我找到最多示例的解决方案。 (使用 std::unordered_set::emplace() 可能是整个复制问题的解决方案,但我还没有完全弄明白。)
  5. 通常,可以通过两种方式提供 'hasher' 和 'equalling' 功能:通过 std::hash<T>and/or std::equal_to<T> 的定义,或通过为这些目的创建函子。有关如何执行此操作的更多信息 here。这些本质上做同样的事情,所以它们可以互换。如第 4 点所述,当您存储直接类型时还有 1 个其他选项,您也可以使用 operator ==,然后在声明 [= 时不需要指定任何 'equaller' 11=].