gcc共享ptr副本分配实现

gcc shared_ptr copy assignment implementation

本文关键字:分配 实现 副本 ptr 共享 gcc      更新时间:2023-10-16

我正在扫描GCC 5中的shared_ptr实现,我看到以下内容:

  __shared_ptr&
  operator=(__shared_ptr&& __r) noexcept
  {
    __shared_ptr(std::move(__r)).swap(*this);
    return *this;
  }

我的问题是,为什么在交换之前临时的额外移动构造?我认为编译将消除任何额外的开销,但为什么不直接调用__r.swap(*this)呢?我遗漏了一些聪明的副作用吗?

我看到类中的其他函数也使用相同的模式来实现,我可以理解接受常量引用,但接受右值引用的情况?

首先,标准就是这么说的,GCC只是不折不扣地遵循它。

这样,赋值操作符有一个__r.empty()的后置条件,这是你的建议无法实现的,所以按照你的建议执行它会产生与标准所说的不同的效果,因此是不符合的。

即该断言成立:

auto p1 = std::make_shared<int>(1);
auto p2 = std::make_shared<int>(2);
p1 = std::move(p2);
assert( !p2 );

"聪明的副作用"是,您创建了一个新的shared_ptr,它最终在交换后保持*this的旧值,然后超出范围。这意味着*this的旧值不会最终出现在__r中。

因为我们需要对引用的右值调用析构函数来减少实例计数。

通过将__r的内脏移动到作为函数return s销毁的临时中,可以确保__r所指的移动对象处于空状态。我想他们想把这个逻辑放在一个地方,移动构造函数,看起来是这样的。

__shared_ptr(__shared_ptr&& __r) noexcept
  : _M_ptr(__r._M_ptr), _M_refcount()
{
  _M_refcount._M_swap(__r._M_refcount);
  __r._M_ptr = 0;
}

就我个人而言,我更喜欢以下实现,据我所知,它是等效的。

widget&
operator=(widget&& other) noexcept
{
  widget temp {};
  swap(*this, temp);
  swap(*this, other);
  return *this;
}

它可以用这样实现的move构造函数来补充。

widget(widget&& other) noexcept : widget {}
{
  swap(*this, other);
}

我假设有

void
swap(widget&, widget&) noexcept;

ADL可以找到的重载并且CCD_ 10的默认构造函数是CCD_。