【问题标题】:Why is there no operator< for class std::weak_ptr? [duplicate]为什么类 std::weak_ptr 没有 operator<? [复制]
【发布时间】:2018-04-09 12:14:13
【问题描述】:

我使用弱指针作为映射中的键。但是,当我尝试编译时,我收到了丑陋的消息,我将其解释为我缺少 std::weak_ptr 的比较运算符,这显然在 std::map 中是必需的,因为它根据它对元素进行排序键值。

然而,现在,weak_ptr 类是一个智能指针类型类,因此,它与指向某些托管数据的指针一起工作。

这个类不提供 operator

您的见解将不胜感激,在这里。 感谢期待。

【问题讨论】:

  • 鉴于弱指针的底层对象随时可能被破坏,比较弱指针完全没有意义。 &lt; 完成指针比较后的皮秒,可能会破坏底层对象之一,因此您的比较结果就出来了。正如答案所声称的,这与一般的指针比较无关。 std::less 强制对指针进行弱排序。查一下。比较普通的指针并没有错。但是弱指针比较,就其本质而言,是完全没有意义的。
  • 好的,谢谢。但是,据我了解,弱指针有一个指向匹配 shared_ptr 实例(s 系列)的“持有者结构”(计算使用它的智能指针的数量,并持有“真实”指针的东西)的指针, 对 ?因此,即使实例计数降至 0,使用原始地址进行比较也不应该那么难——除非实现在指针“过期”时删除了这个非常内部的结构(我不知道,我没有检查 STL 的内部代码。也许我应该)

标签: c++ c++-standard-library


【解决方案1】:

std::owner_less 是在映射中将智能指针排序为键的正确方法。

#include <map>
#include <memory>
#include <string>

struct Foo
{
};

using my_key = std::weak_ptr<Foo>;
using my_comp = std::owner_less<my_key>;

int main()
{
    auto m = std::map<my_key, std::string, my_comp>();

    auto p = std::make_shared<Foo>();
    auto p1 = std::make_shared<Foo>();

    m.emplace(p, "foo");
    m.emplace(p1, "bar");

    p.reset();
    p1.reset();
}

【讨论】:

  • 我不知道这个。很好的发现:)
  • 我也不知道。但是,我很难理解在哪些情况下内部的指针可能是“不同的”。我看到的关于 std::owner_less (或关于 std::weak_ptr.owner_before )的文档说它比较拥有的指针,而不管 get() 的结果如何,并指出这些可能不同“(例如,因为它们指向不同同一个对象中的子对象)”这将如何工作?我的意思是,如果我有指向 Foo 类的智能指针,它们中的一个如何指向“不同的子对象”?
  • @Kzwix 有一个 shared_ptr 的构造函数,它允许您共享受控对象的生命周期,但指向它的子对象。请参阅本页注释 8:en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr
  • 谢谢,理查德,你让我发现了另一件事。但是,文档说它需要一个指向“element_type”的指针。并且“element_type”在参考文献中被定义为类型 T 本身(因为我们还没有在 C++17 中)。但是,构造函数描述明确指出,只要共享指针存在,这个指针就可以是我们想要的任何东西,只要保证它是“活动的”。因此,指向字段或向下转换版本的指针,无论如何。但是,我想知道,如果我的类 Foo 有一个 Bar 类型的字段,如果它需要 Foo* 我怎么能提供一个 Bar* ?
【解决方案2】:

更新:有一个明确的方法来比较std::weak_ptr,请参阅@Richard's answer。我将把我的答案留作历史档案。


实现一个好的operator&lt; 需要从weak_ptr 创建一个shared_ptr 并调用它的operator&lt;。这是

  1. “昂贵”的操作
  2. 当底层shared_ptr 不再存在时未定义。

一般来说,很难获得定义明确且性能良好的 weak_ptrs 排序。

请注意,您始终可以将自定义比较器传递给您的地图对象,您可以按照您想要的任何方式实现该比较器,并仅对该对象保持本地化。但是,接下来由您自己想办法做到这一点。也许您可以使用指向连接的shared_ptr 的控制块的指针,但这是您无法访问的实现细节。所以我真的看不出有什么有意义的方法。

【讨论】:

  • 比较指针值(而不是指针值)不是UB,我认为这是OP想要做的。没有定义operator&lt;实际原因是因为它可能会给出错误的结果,而不是因为它是 UB。 (特别是,不存在的shared_ptrs 可以轻松处理;这就是std::owner_less 所做的。)
  • @KonradRudolph 它可能在某些系统上未定义(并且到处都是实现定义),请参阅DR 1438
【解决方案3】:

我认为关键是(引自 C++ Concurrency in Action,第 194 页,强调我的)

使用像这样的危险指针依赖于这样一个事实,即在指针引用的对象被删除后使用指针的值是安全的。 如果您使用 new 和 delete 的默认实现,这在技术上是未定义的行为,因此您需要确保您的实现允许它,或者您需要使用允许这种用法的自定义分配器。

提供operator&lt;() 需要读取原始指针值,这可能会导致未定义的行为(即,当expired() 返回true 时)。实际上没有办法保证比较成功,除非你先lock() 并检查返回值不为空。

【讨论】:

  • 那本书的作者报告了a defect in the relevant wording,现在标准明确表示比较无效指针值是实现定义的,不一定是未定义的。但这仍然会给weak_ptr 定义比较带来问题。
猜你喜欢
  • 2015-03-14
  • 2010-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-02
  • 1970-01-01
  • 2018-08-13
相关资源
最近更新 更多