假设我们有一个函数,期望按值共享指针.(在现实生活中的例子中,我通过右值引用将其转发给成员.)
void f(std::shared_ptr<Derived> ptr) { ... }
Run Code Online (Sandbox Code Playgroud)
但是我们只有一个指向基类的共享指针,所以我们使用static_pointer_cast:
std::shared_ptr<Base> ptr = std::make_shared<Derived>();
f(std::static_pointer_cast<Derived>(ptr));
Run Code Online (Sandbox Code Playgroud)
第一个赋值(从临时构造ptr)是触发引用计数的原子递增和递减还是共享指针移动?(注意它正在升级.)
在内部static_pointer_cast有一个引用计数的原子增量.如果我们不再需要ptr,我们希望将其移入f.但由于没有超载static_pointer_cast参与右值参考,此举不会产生任何影响:
f(std::static_pointer_cast<Derived>(std::move(ptr)));
Run Code Online (Sandbox Code Playgroud)
一旦ptr被破坏,我们仍然有原子增量和相应的原子减量.为什么没有这样的超负荷?
我可以回答你问题的第一部分,但不能回答第二部分。虽然我不确定标准是否强制要求,但我很确定:
std::shared_ptr<Base> ptr = std::make_shared<Derived>();
Run Code Online (Sandbox Code Playgroud)
不会进行任何无关的引用计数器递增/递减。首先,让我观察一下,这实际上根本不是一个作业,而是ptr. 显然它是由临时建筑建造的,而且显然临时建筑属于不同类型。将匹配的构造函数的签名是(http://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr):
template< class Y >
shared_ptr( shared_ptr<Y>&& r );
Run Code Online (Sandbox Code Playgroud)
注释中写道:
从 r 移动构造一个shared_ptr。构造完成后,*this 包含 r 先前状态的副本,r 为空,其存储的指针为 null。如果 Y* 不能隐式转换为 T*,则模板重载不会参与重载决策
在本例中,YisDerived和Tis Base,很明显我们得到了从Y*to的隐式转换T*,因此构造函数是合法的。严格来说,允许引用计数首先上升到 2,然后再下降到 1 可能是符合要求的。但显然这违背了移动构造函数的全部目的,所以我非常怀疑这就是它的实现方式。