10
votes

I understand that std::unique_ptr is the way it is and probably won't be changed to break backwards compatibility but I was wondering if anyone has a good reason why the writers of the spec didn't overload the get method with a const variant that looks like

const T* get() const;

to follow the intent of the unique_ptr being const.

My best guess is that it is trying to mirror pointers and act like a T* const instead of a typical class. As a follow-up question, if I wanted to hold a pointer in a const-like fashion in a const instance of my class, should I be using something else other than std::unique_ptr to hold the data?

Update

In my case I want to protect myself from misusing the pointer in the class itself. I was writing a const move constructor MyClass(const MyClass&& other) and was copying the data from the new instance into other via std::copy. It took a long time to track down the bug because I had assumed the copy must be correct because of const protection. I'm trying to figure out what I could have done to protect myself from this outside of providing a const getter and using that within the class when doing the copy.

4
Regarding the latter question, wouldn't std::unique_ptr<YourClass const> do what you seek? Or did I misunderstand that part of the question.WhozCraig
In my case my let's say I'm just wrapping some data like struct so all it is is a char[] and an int to represent the length. I want to pass it around and let consumers manipulate the bytes unless they have a const instance. Even if I provide const and non-const getters for the data, I want to ensure my internal methods don't mess with data on a const instance passed in to a method on my classquittle
I do not understand your update. It seems like an XY problem, or completely different question. Why on earth do you want a const move constructor? And what is "const protection"?Oktalist
Your const move constructor does not make any sense, if I understand you correctly, you just implemented a copy constructor. If a move constructor does not make sense in your class that's fine, just don't create one, no need to hack anything...tr3w

4 Answers

6
votes

Smart pointers are pretending to be a raw pointer. If you have class member which is raw pointer and use it in const method that you can't update a pointer, but you can modify object which is pointed. Same behavior is desired for smart pointer. So std::unique_ptr::get is a const method, but doesn't force to return pointer to const object.

Note also that you can have a pointer to const object.

MyClass *pointerToObject
std::unique_ptr<MyClass> smartPointerToObject;

// but you can have also a case
const MyClass *pointerToConstObject
std::unique_ptr<const MyClass> smartPointerToConstObject;

In last case std::unique_ptr::get will return something you are expecting.


Just provide private methods:

InnerClass& GetField() { return *uniquePtrToInnerClass; }
const InnerClass& GetField() const { return *uniquePtrToInnerClass; }

And use it in your code and you will have const object of inner class in const method.

5
votes

There's no point to giving read-only access to an object via its unique_ptr. You only pass unique_ptr around when you are transferring ownership, for access to the object without an ownership transfer, call up.get() and pass a const T* to the function that should only read (or if the pointer is never nullptr, it's also reasonable to evaluate *(up.get()) and pass a const T&).

As a bonus, this allows you to use that function with objects stored on the stack, embedded inside another object, or managed with a smart pointer other than unique_ptr.

There's a good discussion of all the unique_ptr parameter passing cases (in/out, const/non-const, etc) here:

5
votes

For the same reason a T*const when dereferenced is a T&, not a T const&.

Constness of pointer is distinct from pointness of pointed-to.

get is const, it does not modify the state of unique_ptr.

Its constness does not impact the constness of the contents.

There is the idea of smart pointers that propogate constness, but unique_ptr is not that beast.

std::experimental::propogate_const wraps a pointer-like object and makes const travel through it.

It, or something like it, may solve your problem.

Note that I find half the time when I try to have const propogate like this, I discover I was wrong. But this may not be the case here.

In general, the proper way to handle the guts of a T*const in a const manner is to pass a T const& (or the nullable variant T const*).

0
votes

I think this concern is valid, there should be 2 versions for each de-referencing functions,

e.g.
    const T* get() const;
    T* get();
    enter code here

I know that purpose of providing "T* get() const" is to ease replace existing raw pointer usages.

But since uniq ptr denotes ownership, it is incorrect that some one being able to modify some thing OWNED by the object via a immutable(const) reference [assuming modifying something fully owned by a object is same as modifying the object itself - which is true if this was an object instead of a ptr].

May be best option would be std to provide another version of Uniq ptr which holds to above idiom (only other option may be to derive a new class from uniq ptr and provide 2 versions for de-referencing )