我经常面临这样的争论,即通过接受const std::shared_ptr<T>&
可以避免引用计数增加:
void foo(std::shared_ptr<const int> p);
void foo2(const std::shared_ptr<const int>& p);
int main(){
std::shared_ptr<const int> a = std::make_shared<int>(3);
foo(a); // calling this function always does reference counting (atomic locks...)
foo2(a); // calling this function elides reference counting
std::shared_ptr<int> b = std::make_shared<int>(3);;
foo2(b); // what happens here? since there is a cast involved creating a temporary ... (NRVO??)
}
但是我认为在调用foo2(b)
时没有省略引用计数?但是,编译器或 std 实现能否以某种方式避免引用计数。如果foo2(std::move(b))
被召唤,让这种省略发生,会不会更好?
是的,在进入foo2
之前必然会有引用计数增加,在退出之前必然会减少。这是因为参数const std::shared_ptr<const int>& p
必须引用类型为std::shared_ptr<const int>
的不同对象,因此必须构造和销毁一个临时对象。
在语言中,不可能省略此引用计数,因为如果在执行foo2
期间修改了参数b
,则参数p
必须不受影响;它必须继续指向堆上的同一int
对象,该对象可以修改但不能删除。(当foo2
与a
一起调用时,这不适用,因为在这种情况下,p
直接指a
,而不是临时的,因此对a
的修改可以通过p
可见。
将std::move(b)
传递给foo2
不是一个好主意,因为这会将b
留空,并在临时绑定到p
时删除int
对象。
std::shared_ptr<const int>
是与std::shared_ptr<int>
不同的类型,但有一个隐式转换构造函数。
当你调用foo2(b)
时,一个临时std::shared_ptr<const int>
是由b
构造的,并绑定到p
。构造函数递增引用计数,析构函数递减引用计数。
当您调用foo1(a)
时,a
会复制到p
中。p
在调用期间存在,然后被销毁。构造函数递增引用计数,析构函数递减引用计数。
当你调用foo2(a)
时,a
绑定到p
。不会构造临时,因此不会更改引用计数。
请注意,您的示例中没有引用计数,因为任何指针都没有指向int
s 或const int
s。