Passing std::unique_ptr as const ref

Started by
28 comments, last by Juliean 7 years, 3 months ago

Hi all,

Since a while I've started to work more with std::unique_ptr, which gives me control on when to construct an object etc.

Now in some cases some class needs a const ref to an object which is created as a std::unique_ptr.

I've been searching on how this should be handled, from what I understand, it's perfectly fine/ OK to pass the std::unique_ptr as *myPtr, when a const ref is expected.

From practice/ playing around I see that this works.

My question; would you see any cons in doing it this way?

Any input is appreciated, like always.

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

Advertisement

Hi all,

Since a while I've started to work more with std::unique_ptr, which gives me control on when to construct an object etc.

Now in some cases some class needs a const ref to an object which is created as a std::unique_ptr.

I've been searching on how this should be handled, from what I understand, it's perfectly fine/ OK to pass the std::unique_ptr as *myPtr, when a const ref is expected.

From practice/ playing around I see that this works.

My question; would you see any cons in doing it this way?

Any input is appreciated, like always.

If you are dealing with C api or external libraries you must use the const ref variant.

In design perspectives, it's usually better to pass a unique ptr, because this clerifies the usage of the pointer (which is the goal of smart pointers).

I don't recommend passing regular pointers (or const *) when you clearly has a Unique_ptr.

If you really have to share the memory (In terms of memory owners) then use a shared_ptr instead of passing the raw pointer.

If a method or class doesn't need to worry about ownership semantics, then there's no need to unnecessarily bloat your interfaces with std::unique_ptr and friends (which introduces additional maintenence cost if that changes to a different smart pointer type). It's perfectly fine to use raw pointers if ownership is handied properly at the design level, so you can make simplifying assumptions during actual implementation.

I always worry when I see too many smart pointers being passed around. It gives me the impression that ownership handling wasn't really well thought out, and that the smart pointers are being used as a crutch to make sure everything still works.
What Zipster said, x10000.

Smart pointers are there to describe ownership semantics; passing around const refs to unique_ptrs doesn't make a huge amount of conceptual sense - the thing you are passing to can't own, so all you are doing it making a meal of the function signature for no good reason.

Even shared pointers are a bit of an iffy case; often just thrown in because people aren't sure when generally correct design can resolve the majority of cases.

If you are't taking ownership of an object then don't pass in smart pointers would be my default setting - there is nothing wrong with a raw pointer when the design calls for it.

Thanks guys.

Ownership of the pointer isn't an issue/topic here, so I'll just pass the raw ptr of the unique_ptr to functions where a const ref is expected. I don't want to pass an actual unique_ptr in these cases. For example, I have a IDevice class that has a 'SetRenderTarget' function, which takes a const ref to a IRenderTarget. And when the 'calling' code has a std::unique_ptr for the IRendertarget object, I'll pass *myRT to pass the object to the IDevice function (which expect a const ref).

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

Ownership of the pointer isn't an issue/topic here

It is. That's what Zipster was trying to explain. It is perfectly fine to pass a reference to an unique pointer from a technical point of view. It just isn't 100% "correct" in semantics.
A unique pointer not only manages the resource but also communicates that your intent is there shall be exactly one such pointer which owns the resource. You can move it, but not copy. If you move it, you must do it explicitly, and you thereby communicate to the human reader that you wish to transfer ownership.

When you create a reference, you're cheating. You are creating a "copy" without creating one. The reference is merely an alias with a different name, sure, so technically this is perfectly correct. But it looks like something different.

I don't want to pass an actual unique_ptr in these cases.

You cannot even do that even if you wanted, the class is designed to not allow this (that's the major difference to it's predecessor auto_ptr). You can only move the unique pointer, which however turns the function into a sink.
Now you can be especially devious and create a reference to the unique pointer, and then move the unique pointer to your function. That, too, is perfectly "correct" (until you access the reference). Only just, when the function returns, the unique pointer that your reference aliases will have been destroyed. Good luck trying to debug that, it might even accidentially work for a while.

Long story short: References to smart pointers are not the most awesome idea in the world.
So I get that passing references to unique_ptr's isn't very good. But what about passing collections (say a vector) of unique_ptrs? If I have objects stored in a vector as smart pointers, wouldn't it be problematic to have to make a new vector with raw pointers just to pass the vector to a function?

I've been searching on how this should be handled, from what I understand, it's perfectly fine/ OK to pass the std::unique_ptr as *myPtr, when a const ref is expected.

As others already have explained, this is fine. Just as a comment, I use myPtr.get() instead of *myPtr for extra clarity.

I'd just like to add something I've once read in stack overflow, that helped me a lot in these concerns on which way to store data, and which way to pass them around. They're just guidelines:

- Using smart pointers to point out ownership

- Receiving raw pointers or references when an object does not affect the ownership of the data. Instead, the design must ensure that the data will outlive the non-owning objects (even in multithreading)

- Use non-references when nullptr is a valid value (and should not cause an error message, nor activate an assert)

Overall, I felt like it beneficially reduced the amount of decisions to be made, every new method...

Just for future reference, when adding .get() in the sample case above, you still have to use * to identify the pointer. So: *myPtr.get()

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

This topic is closed to new replies.

Advertisement