I was suggested to replace in few places passing const shared_ptr by value to passing by reference (e.g. change const std::shared_ptr<T> param to const std::shared_ptr<T>& param) when I was checking my project with PVS-Studio (http://pvs-studio.viva64.com), the static code analysis tool.
I understand that for performance hot-spots it might be a good optimization as creating a temporary copy of shared_ptr requires updating reference counter which is a interlocked operation.
So the question is - how safe is this optimization? Are there any cases when it could not be used?
If you have a parameter with the const midifier:
If it is a primitive type, never use const or passing by reference unless you need to use pass by non-const-reference. The const modifier is useless for parameters that aren't passed by reference. Passing primitives by reference is slower.
If it is not a primitive type, passing by reference or const reference is faster because the copy constructor does not have to be called to copy the object. Passing by const reference rather than non-const reference allows for you to use temporaries as parameters such as rvalues. Non-const reference passing requires an existing variable.
So the question is - how safe is this optimization?
Safe
Are there any cases when it could not be used?
In a multi-threaded environment where the shared_ptr being passed as a reference to a process of some other thread, if the life of that shared_ptr could go out of scope before the lifetime of the other thread is complete, the destructor of the shared pointer would free the memory that it points to and you would be in undefined territory. This would be a case where passing by value would be advantageous, because the reference counter would be incremented, and when the shared_ptr from the other thread's life end, only the counter will be decremented, but your copy will still live on.
If this is singly threaded, I cant think of any reason why this optimization would not work.
I would suggest you never pass reference counted objects by reference. It's not always immediately clear why the reference count can hit zero, but when it does it's just one more problem to solve.
So for me it looks like passing shared_ptr by const reference has exactly the same limitations as passing anything else by const reference. E.g. if anything is passed to different thread life-time of the object should be carefully considered, and the same case for storing of the pointer.
Yes, but you can still make a copy of the const reference shared_ptr later if you need that kind of functionality.
edit: To kbw point, we do usually pass the raw data vs a reference to the shared_ptr in these types of cases, for no other reason than people tend to be more careful with raw pointers/references than shared_ptr, otherwise we just pass it by value
@kbw > I would suggest you never pass reference counted objects by reference.
In general, I tend to agree with kbw.
Though there are experts who vehemently disagree.
There's nothing special about shared_ptr when it comes to whether you pass it by value, or pass it by reference. Use exactly the same analysis you use for any other user defined type. ... it's expensive to pass it by value, so if I can get away with it with proper semantics in my program, I'm gonna pass it by reference to const or reference instead. - Scott Meyers
... always pass them by reference to const, and very occasionally maybe ... maybe then you might pass by value... - Herb Sutter
Thinking more about the problem I would agree with Herb.
I can't think of any scenarios that will cause problems (scenarios that WILL cause problems if we will replace shared_ptr<T> with anything else I ignored, as these are problems related to references, not the shared_ptr).
Also according to my benchmark passing by value is about 10 times slower for std::shared_ptr, and more than 8.5 times slower for boost::shared_ptr.
So I see no reason why we should not take passing shared_ptr by reference as a good habit (that we use for other types, not only in performance-critical code)