为什么std::weak_ptr没有operator->运算符?

10
可以这样实现:
  std::shared_ptr<T> operator->() {
      auto shared = lock();
      if(shared == nullptr) {
          throw std::bad_weak_ptr(); // or some other exception
      }
      return shared;
  }

实时演示

为什么weak_ptr的作者决定不使用operator->?(他们一定想过)

我可以想到潜在的原因,但我想知道官方原因是什么,如果存在的话。潜在原因:

  • 防止多次调用引用计数的额外增量/减量
  • 鼓励显式锁定而不是(有些隐藏的)异常

如果您对返回的shared_ptr的生命周期感到困惑,请参见此文献。

另外,有人问为什么会使用weak_ptr,如果您希望它不过期?答案:循环引用。

2个回答

10

原始提案weak_ptr并没有包括operator->的重载。

我没有查看过每个会议的记录,但已经关注了讨论的内容,也不记得有人提出应该添加它。因此,“官方”原因是它不存在的主要原因可能是没有人提出将其添加。

如果您想回到最初,大多数情况源于John Ellis和David Detlef在1994年Usenix的Safe, Efficient Garbage Collection for C++论文中。其中包括附录B中的weakptr类型。这有些不同(weakptr::pointer直接返回指针,或者如果指向对象已被销毁,则返回空指针),但仍然没有使用运算符重载来完成任务。

Greg Colvin撰写了counted_ptr添加到标准中的原始提案。它的counted_ptr 基本上等同于现在所称的shared_ptr,但没有包括任何类似于weak_ptr的东西。

委员会在拒绝了counted_ptr提案并改而采用auto_ptr之后不久,counted_ptr的基本思想在Boost上得到了复兴。我不记得曾经看到过任何关于将operator->添加到其中的讨论,但它在那里“存活”了很长时间,因此完全有可能有人提出了它,而我并不知道。


2
但就我所看到的,所提出的 operator-> 正好符合您所描述的:将其转换为 shared_ptr,并仅在成功访问对象时允许访问(否则会抛出异常)。这有什么不好的呢? - Mike Seymour
@MikeSeymour:你想要保留shared_ptr,并在其上调用方法,而无需支付同步的成本。 - Alexandre C.
3
有时您需要进行手动转换,这时手动转换仍然可用。有时您只想访问单个成员,在这种情况下,手动转换只会产生噪音,而所建议的运算符可以使代码更加简洁。 - Mike Seymour
甚至更早的boost文档中关于weak_ptr的最早版本我能轻易找到的是2003年2月10日。这里也没有operator->。版权声明包括“版权所有1999年Greg Colvin和Beman Dawes,版权所有2002年Darin Adler,版权所有2002年、2003年Peter Dimov”,这与您链接的原始提案的作者相吻合。在此处有一些旧文档的片段,1999年提到了智能指针。版本1.20可能会有所启发? - Yakk - Adam Nevraumont
啊,Boost版本1.20有一个shared pointer,但乍一看没有weak pointer。所以我们可以找到第一个添加weak pointer到Boost的版本。而上述内容证明了std::shared_ptr来自于Boost(提案的作者和论文本身)。 - Yakk - Adam Nevraumont

7
我来尝试给出为什么这不是一个好主意的好理由:
首先,问题在于清晰度:
ptr->foo();
ptr->bar();

问题在于,在第一次和第二次调用之间,指针可能会到期,可能是由于不同的线程(这将是竞争条件),或者由于对 foo 的调用的副作用。
另外一个问题是对称性:当我有一个指针时,我希望操作符 *-> 和隐式转换为布尔值。有些人可能不同意,但操作符 *-> 通常是相互关联的。如果这里不是这种情况,我会感到惊讶。
话虽如此,使用 C++11,编写起来就太容易了:
if (auto p = ptr.lock()) {
    p->foo();
    p->bar();
}

如果知道ptr是一个weak_ptr,那么这段代码的意义和行为就非常清晰。


1
在进一步询问后,我认为你关于对称性的观点是正确的。拥有operator->会使weak_ptr在缺少最基本的operator*时更像一个实际指针。 - Taylor
1
我也意识到这是一个很好的理由。简而言之,weak_ptr不是指针,所以它不必像指针一样表现。也许weak_ref会是更好的选择,但引用这个术语也被使用了。 - Ulrich Eckhardt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接