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