[shared_ptr] calling functions by reference

This might be a unimportant question and may have already been answered many times before, but I was wondering about functions that take shared pointers as arguments. Is there ever a reason I would not want to use a shared_ptr reference as an argument? I can't think of any, but is there any? class XYZ; void foo( shared_ptr< XYZ > &xyz ) { }

On Sat, May 28, 2011 at 10:36 PM, Sid Sacek <ssacek@securewatch24.com>wrote:
This might be a unimportant question and may have already been answered many times before, but I was wondering about functions that take shared pointers as arguments. Is there ever a reason I would not want to use a shared_ptr reference as an argument? I can't think of any, but is there any?
One common reason given to pass by value is to implicitly handle the case where you call a separate function which may release your shared pointer as a side effect. For instance: ////////// shared_ptr< int > a( new int( 2 ) ); void foo() { a.reset(); } void bar( shared_ptr< int > const& arg ) { foo(); *arg = 0; // Kaboom } int main() { bar( a ); } ////////// Note that while here the issue may seem obvious, similar problems can be much more subtle in practice (for instance, you may be calling a user-provided function object, etc.). If you had passed the smart pointer by value then this code would not be problematic. Still, it is my opinion that you should generally prefer passing by reference, just as you would most other types with non-trivial copy operations. Copying a shared_ptr is far from free. If you encounter a situation similar to the above, then just copy the shared_ptr in that particular case. -- -Matt Calabrese

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Matt Calabrese Sent: Saturday, May 28, 2011 10:53 PM To: boost@lists.boost.org Subject: Re: [boost] [shared_ptr] calling functions by reference
One common reason given to pass by value is to implicitly handle the case where you call a separate function which may release your shared pointer as a side effect. For instance:
////////// shared_ptr< int > a( new int( 2 ) );
void foo() { a.reset(); }
void bar( shared_ptr< int > const& arg ) { foo(); *arg = 0; // Kaboom }
int main() { bar( a ); }
I agree with you that your example above will crash the program, but calling Reset() is micro-managing the smart-pointer. There should be no good reason to call reset on a regular basis, and only have objects go out of scope. That is my understanding of the point of the shared_ptr<>. Let's assume that reset is never used, and shared_ptr<> is not set to a global variable, then the only thing that comes to my mind is that there might be a multi-threading subtlety that I'm not aware of. For example, suppose thread-A created an object and passed it to thread-B, which then calls Foo(shared_ptr<>&). I started to think about what happens when the original thread-A releases its pointer while function Foo() is still running inside thread-B. However, there seems to be no way that the shared_ptr<> object can be passed from one thread to another without making a copy of the shared_ptr<>. That means each thread owns its own copy of the shared_ptr<>, thereby preventing any multi-threading issues from happening. During an application shutdown, the master thread would communicate with all other threads to perform an orderly shutdown, and when all auxiliary threads have gone away, the master thread may want to call reset on some of these managed resources, but at this point, there is no possibility of threads stepping on each other. This seems like a disciplined approach to using shared_ptr<> to me. I figured that with all of the experienced coders out there, that there may have been a very surprising subtlety that was not anticipated, but which ended up braking their program. This is the sort of information I'm seeking regarding shared_ptr<> references. -Sid Sacek

On Sun, May 29, 2011 at 12:18 AM, Sid Sacek <ssacek@securewatch24.com>wrote:
I agree with you that your example above will crash the program, but calling Reset() is micro-managing the smart-pointer. There should be no good reason to call reset on a regular basis, and only have objects go out of scope. That is my understanding of the point of the shared_ptr<>.
It's perfectly fine and correct to call reset and I would hardly call it micro-managing. In fact, I'd call it good practice to reset. You should generally control your resource cleanup and not leave dangling smart pointers around when you are done with whatever they point to. Immediate, deterministic disposal is often very important.
Let's assume that reset is never used, and shared_ptr<> is not set to a global variable, then the only thing that comes to my mind is that there might be a multi-threading subtlety that I'm not aware of.
The example I used only had it as a global for simplicity. The same situation can and does come up without any globals at all in a single-threaded environment and even when reset is not used (the pointer can simply be assigned to point somewhere else and the issue I showed still exists). -- -Matt Calabrese

Sid Sacek wrote:
-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Matt Calabrese Sent: Saturday, May 28, 2011 10:53 PM To: boost@lists.boost.org Subject: Re: [boost] [shared_ptr] calling functions by reference
One common reason given to pass by value is to implicitly handle the case where you call a separate function which may release your shared pointer as a side effect. For instance:
////////// shared_ptr< int > a( new int( 2 ) );
void foo() { a.reset(); }
void bar( shared_ptr< int > const& arg ) { foo(); *arg = 0; // Kaboom }
int main() { bar( a ); }
I agree with you that your example above will crash the program, but calling Reset() is micro-managing the smart-pointer. There should be no good reason to call reset on a regular basis, and only have objects go out of scope. That is my understanding of the point of the shared_ptr<>.
If you intend to pass by reference, why use a shared_ptr in the first place? Bo Persson

On Sun, May 29, 2011 at 6:28 AM, Bo Persson <bop@gmb.dk> wrote:
If you intend to pass by reference, why use a shared_ptr in the first place?
Passing the shared pointer by reference doesn't change any of the reasons for why you'd use one to begin with. As always, you use shared pointers to have shared ownership of the pointee. Passing the shared pointer by reference or passing the pointee by reference directly just means that you do not (at least not immediately) want to create a new smart pointer that can extend the lifetime of the pointee. You still may want a reference to a shared pointer for several reasons, such as for internally storing a copy of the smart pointer in an object, creating a weak_ptr from it, etc. You don't want to needlessly copy smart pointers just as you don't want to needlessly copy any other object in C++. Working with shared pointers does not change any of the rationale for why one would generally pass an object by reference in C++. -- -Matt Calabrese

On Sat, May 28, 2011 at 10:36:47PM -0400, Sid Sacek wrote:
This might be a unimportant question and may have already been answered many times before, but I was wondering about functions that take shared pointers as arguments. Is there ever a reason I would not want to use a shared_ptr reference as an argument? I can't think of any, but is there any?
class XYZ;
void foo( shared_ptr< XYZ > &xyz ) { }
Concurrent access to different shared_ptr instances in the same family (sourced from the same source) is safe. Concurrent access to a single shared_ptr instance is generally unsafe. Passing the shared_ptr by reference puts the onus on you to ensure that the shared_ptr instance you pass in is valid for the duration of the function call, which is non-trivial in general. Examples of the above would be foo(some_global_ptr); or foo(bar->baz); where some_global_ptr is destroyed somewhere deep in the call chain, or bar is destroyed during the call sequence. I've had real-life bugs of this kind. It's not pretty. Unless you have a genuine reason to access the same instance, try to avoid passing it by reference-to-non-const. If you feel you need to avoid the non-free copying cost, pass it by reference-to-const. -- Lars Viklund | zao@acc.umu.se

On Sun, May 29, 2011 at 12:09 AM, Lars Viklund <zao@acc.umu.se> wrote:
I've had real-life bugs of this kind. It's not pretty. Unless you have a genuine reason to access the same instance, try to avoid passing it by reference-to-non-const. If you feel you need to avoid the non-free copying cost, pass it by reference-to-const.
Passing by reference to const generally does not solve all problems either. In both cases, it's still perfectly valid for other contexts to already hold the object directly or by reference to non-const and for the value to be modified via side-effects of further function calls. -- -Matt Calabrese

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Lars Viklund Sent: Sunday, May 29, 2011 12:10 AM To: boost@lists.boost.org Subject: Re: [boost] [shared_ptr] calling functions by reference
Concurrent access to different shared_ptr instances in the same family (sourced from the same source) is safe. Concurrent access to a single shared_ptr instance is generally unsafe.
You are confirming my suspicion about multi-threading issues.
Passing the shared_ptr by reference puts the onus on you to ensure that the shared_ptr instance you pass in is valid for the duration of the function call, which is non-trivial in general.
I understand. But doesn't it help to have guidelines and standards when it comes to working with shared_ptr<>?
Examples of the above would be foo(some_global_ptr); or foo(bar->baz); where some_global_ptr is destroyed somewhere deep in the call chain, or bar is destroyed during the call sequence.
If I understand you correctly, somebody performed a reset() on something along the call sequence. Would I be correct in saying that if reset() isn't used in a busy call sequence, then this problem wouldn't arise? Wouldn't using a const-reference, like you mention below, identify potential problems like that?
I've had real-life bugs of this kind. It's not pretty. Unless you have a genuine reason to access the same instance, try to avoid passing it by reference-to-non-const. If you feel you need to avoid the non-free copying cost, pass it by reference-to-const.
-- Lars Viklund | zao@acc.umu.se

On Sun, May 29, 2011 at 12:51:44AM -0400, Sid Sacek wrote:
-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Lars Viklund Sent: Sunday, May 29, 2011 12:10 AM To: boost@lists.boost.org Subject: Re: [boost] [shared_ptr] calling functions by reference
Concurrent access to different shared_ptr instances in the same family (sourced from the same source) is safe. Concurrent access to a single shared_ptr instance is generally unsafe.
You are confirming my suspicion about multi-threading issues.
The above is paraphrased from the documentation, which is more precise than me, so do read the documentation on thread safety of shared_ptr: http://www.boost.org/doc/libs/1_46_1/libs/smart_ptr/shared_ptr.htm#ThreadSaf...
Passing the shared_ptr by reference puts the onus on you to ensure that the shared_ptr instance you pass in is valid for the duration of the function call, which is non-trivial in general.
I understand. But doesn't it help to have guidelines and standards when it comes to working with shared_ptr<>?
Guidelines are fine things indeed but they are dangerous if you do not cover the rationale for them. My set of guidelines here (and for most other parameter passing) are: * pass by value when affordable; * pass by reference-to-const-T when copies are not affordable; * pass by reference-to-T where mutation of the argument is required.
Examples of the above would be foo(some_global_ptr); or foo(bar->baz); where some_global_ptr is destroyed somewhere deep in the call chain, or bar is destroyed during the call sequence.
If I understand you correctly, somebody performed a reset() on something along the call sequence. Would I be correct in saying that if reset() isn't used in a busy call sequence, then this problem wouldn't arise? Wouldn't using a const-reference, like you mention below, identify potential problems like that?
Someone could have replaced an ancenstor of the shared_ptr object. You do not need to explicitly invoke reset to reduce lifetimes. In the example above, bar could be modified by something you call. References-to-const does not limit what other entities can do, but restricts what _you_ and whoever you call directly can do to your particular view of the instance. It is impossible for you to assign to (or reset() or any other mutation of) the shared_ptr through a reference-to-const, nor can anyone your reference is passed to.
I've had real-life bugs of this kind. It's not pretty. Unless you have a genuine reason to access the same instance, try to avoid passing it by reference-to-non-const. If you feel you need to avoid the non-free copying cost, pass it by reference-to-const.
As above, reference-to-const is not a panacea, but every little bit of aid helps, as it eliminates one class of faults mentioned elsewhere in the thread as well (assigning to it, reset()ing it). -- Lars Viklund | zao@acc.umu.se

On Sun, May 29, 2011 at 4:36 AM, Sid Sacek <ssacek@securewatch24.com> wrote:
This might be a unimportant question and may have already been answered many times before, but I was wondering about functions that take shared pointers as arguments. Is there ever a reason I would not want to use a shared_ptr reference as an argument? I can't think of any, but is there any?
Yes, I think. If the function makes a copy of the argument, passing by value might allow you to take advantage of move semantics (if the callee doesn't need the argument anymore), whereas passing by (const) reference doesn't. Olaf

Thank you. I appreciated all of the insight from all. -----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Olaf van der Spek Sent: Sunday, May 29, 2011 7:06 AM To: boost@lists.boost.org Subject: Re: [boost] [shared_ptr] calling functions by reference On Sun, May 29, 2011 at 4:36 AM, Sid Sacek <ssacek@securewatch24.com> wrote:
This might be a unimportant question and may have already been answered many times before, but I was wondering about functions that take shared pointers as arguments. Is there ever a reason I would not want to use a shared_ptr reference as an argument? I can't think of any, but is there any?
Yes, I think. If the function makes a copy of the argument, passing by value might allow you to take advantage of move semantics (if the callee doesn't need the argument anymore), whereas passing by (const) reference doesn't. Olaf _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
participants (5)
-
Bo Persson
-
Lars Viklund
-
Matt Calabrese
-
Olaf van der Spek
-
Sid Sacek