【问题标题】:Why is there no overload of bool std::operator==(T1* a, std::shared_ptr<T2> b)?为什么没有 bool std::operator==(T1* a, std::shared_ptr<T2> b) 的重载?
【发布时间】:2014-05-06 21:13:33
【问题描述】:

我注意到 stdlib 没有为 shared_ptr 和原始指针的相等运算符提供重载。如果你有一个 std::unordered_set&lt;std::shared_ptr&lt;Foo&gt;&gt; 并且你想通过传递一个原始的 Foo* 来擦除元素,这尤其不方便

是否有任何具体原因没有这样的过载?在我看来,它很容易实现:

template<typename T1, typename T2>
bool operator==(const T1 * a, const std::shared_ptr<T2> & b) {
    return a == b.get();
}

...这个实现会有什么危险或意外吗?

【问题讨论】:

  • 我对规范的记忆很差,但我相信围绕不同类型的指针有一些警告。有什么理由不应该是operator==(const T * a, const std::shared_ptr&lt;T&gt; &amp; b)
  • 我认为 OP 想要比较两个指针而不考虑类型,只是为了查看原始内存位置是否与共享指针中的内容匹配。从某种意义上说,这就像拥有一个指向 int 的指针和一个指向 float 的指针并将它们都转换为 void * 以查看它们是否相同。不知道为什么有人会想采取这种方法。
  • 其实,不,我不打算比较任意指针。如果 T1 和 T2 不是相同的继承层次结构,这甚至不会编译。

标签: c++ c++11 operator-overloading shared-ptr


【解决方案1】:

对于unordered_set,您不仅需要==,还需要hash 才能工作。

对于set,透明比较器可让您通过非关键类型的事物来查找事物。 set 默认是不透明的,这样操作符就没有用了。

添加自己的比较器后,您可以为其覆盖==

遗憾的是,unordered_set 目前没有透明选项。为了在 unordered_set 中查找/擦除/等内容,您必须拥有密钥类型。

你的超载是有问题的。更好的是:

template<class U, class...Ts>
auto operator==( U const* lhs, std::shared_ptr<Ts...> const& rhs )->decltype( lhs == rhs.get() ) {
  return lhs == rhs.get();
}
template<class U, class...Ts>
auto operator==( std::shared_ptr<Ts...> const& lhs, U const* rhs )->decltype( lhs.get() == rhs ) {
  return lhs.get() == rhs;
}

但请注意,编写上述内容需要 C++11 支持。缺少上述内容的解决方案最终要么写得一团糟(使用 SFINAE),要么声称在没有的类型之间存在 ==(并且其他 SFINAE 代码无法实现它为时已晚)。

但是&lt; 呢?好吧,在原始指针上&lt; 在同一个对象中只是一个好主意,因此在共享指针和原始指针之间公开它似乎是一个可怕的主意。所以现在我们正在接近std::less,并希望通过智能指针和非智能指针之间的透明支持来增强它。

我们是否还支持混合智能指针(sharedunique?)——天真地,您可能会说不,但如果您替换删除器,unique_ptr 不一定代表内存所有权! (shared_ptr 也是如此)。是否应该将具有不同删除器的智能指针视为相等或等效?根据删除者的不同,数据的含义可能会有很大的不同。

现在,鉴于我在短短几分钟的工作中设法提出了棘手的问题,并且这样的过载会鼓励混合原始指针和shared_ptr 它甚至无助于解决你想用它解决的问题,也许这不是一个好主意。

也许是这样。这导致了它不在标准中的真正原因。

没有人提出并被接受。

如果您认为这是个好主意,我鼓励您研究提案流程。检查其他提案,找出正确的步骤,然后一起提出提案。

如果这看起来太吓人,您可能想要创建一个提供上述比较运算符的强大库,并将其用于 boost,也许人们会使用它并说“哇,我们一直需要它!”

【讨论】:

  • 如果你有std::shared_ptr,你就有C++11,所以这不是一个不合理的限制。还是不错的分析。 +1
  • @msalters true,除了共享 ptr 在 tr 中并且主要在 boost 中:在这两种情况下,基于 rerun type deduction based sfinae 直到最近才出现。
  • shared_ptr 没有根据其删除者的类型进行模板化。
  • @t.c.观点。留下Ts... 用于理论上的未来扩展(我怀疑会发生)。对大脑有独特的影响。
【解决方案2】:

如果您使用的是 shared_ptr,那么传递原始指针是非常危险的,因为如果您有持有该原始指针的东西,则根据定义,它会破坏 shared_ptr 的引用计数概念。没有过载的原因可能是为了阻止这种情况。

【讨论】:

  • shared_ptr::get() 存在,但是。只要已知其生命周期小于至少一个shared_ptr 的生命周期,非拥有指针就可以了。
  • 确实如此,但我认为这篇文章的含义是不能保证原始指针的生命周期会更短。通常当一个人使用 get() 和 shared_ptr 时,他们正在做一些普通的事情,比如 memcopies。
  • @AlanStokes shared_ptr::get() 的存在与 stix 所说的并不矛盾:不鼓励使用原始指针,而不是不可能。当你设计一个 API 时,这是一个重要的区别:不要隐式/静默地允许潜在危险的事情,让它们显式。这样一来,API 的用户就承认他确实打算做某事,而这不仅仅是一个意外。
  • @AlanStokes 此外,IIRC 实现可以分配shared_ptr::get() 指向的T 对象以及引用计数器和其他管理材料;结果将是,如果您滥用get 指向的内存(例如delete-ing 它或其他东西),您可能(或可能不会,或有时)得到灾难性的结果。
猜你喜欢
  • 1970-01-01
  • 2017-08-15
  • 1970-01-01
  • 2020-05-03
  • 1970-01-01
  • 1970-01-01
  • 2023-02-02
  • 2021-05-16
  • 2010-11-09
相关资源
最近更新 更多