12

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.

8
  • Regarding the latter question, wouldn't std::unique_ptr<YourClass const> do what you seek? Or did I misunderstand that part of the question. Commented May 18, 2017 at 16:22
  • 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 class Commented May 18, 2017 at 16:27
  • Related: stackoverflow.com/a/24428465/103167 Commented May 18, 2017 at 16:29
  • 1
    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"? Commented May 19, 2017 at 12:44
  • 1
    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... Commented May 21, 2017 at 18:52

5 Answers 5

6

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.


Based on comment below:

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.

Sign up to request clarification or add additional context in comments.

3 Comments

I want the contents to be mutable event the instance is mutable and non-mutable when it isn't so I can't know when instantiating the class or storing the data whether or not to store it as a const or non-const version.
std::unique_ptr<const MyClass> doesn't fulfill your needs? Also you write such complex sentence in comment that I'm unable to understand it.
it doesn't fulfill by needs. Consider there's Outer and Inner and Outer contains a unique_ptr<Inner>. Outer needs to manipulate the Inner instance sometimes but it would go against functionality of Outer to manipulate Inner within a const method.
5

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:

3 Comments

The problem I'm trying to solve is not passing the unique_ptr around, it's protecting me from myself within the class when accessing a const instance. I could add a const get for every smart pointer in every class to always protect myself, but that's a lot of extra code.
I guess what he's getting at is you can mutate the internals of a unique_ptr member from a const member function, even though that member function is marked const. You couldn't do that if it weren't for the unique_ptr (eg if you stored a raw pointer instead of the unique_ptr)
@bobobobo: If you had a raw pointer, you could still mutate the pointed-to object. The pointer type would be T* const not const T*. You couldn't replace the pointer (but get() is not proposing to replace the pointer)
5

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*).

4 Comments

std::experimental::propagate_const<std::unique_ptr<T>> ?
You're just repeating the question, which said "My best guess is that it is trying to mirror pointers and act like a T* const instead of a typical class. "
propogate_const looks somewhat promising but in my case using it would result in the strange pattern of always wrapping calls to unique_ptrs in this call when accessing them in const methods. That is a bit closer to what I want but still rather verbose to use across every class that uses a unique_ptr
@quittle This "deep const" is not supposed to be an "everywhere thing"? You can write an alias in any case -- template<class T> using flat_ptr = std::experimental::propotate_const<std::unique_ptr<T>>. This class only makes sense to use within classes that contain a logically const pointer; other uses of unique_ptr shouldn't use it.
1

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 )

Comments

0

Because as far as the unique_ptr is concerned, getting the internal raw pointer reference is a const operation. Calling .get() and retrieving the internal raw pointer of a std::unique_ptr does not change the internal state of the std::unique_ptr object itself. So it seems the library designers elected to mark it const without attention to what could happen to the underlying object if they just return a straight non-const reference to it.

In fact, if you have a std::unique_ptr inside an object, and you call a const member function of that object, you can still call non-const member functions on the internal std::unique_ptr inside that object. For example:

struct A {
    void Const() const { }
    void nonConst() { }
};

struct B {
    std::unique_ptr<A> a;
    void go() const {
        a->nonConst(); // OK
    }
};

Although you cannot perform non-const operations on the internal state variables of an object from one of its const member function, there is no rule that says you cannot perform non-const operations on other objects.

What you may be expecting is the constness promise to carry over from the unique_ptr to also apply to access to what it internally points to, so you'd expect unique_ptr to be written something like this:

template <typename T>
class cunique_ptr {
    T* ptr;
public:
    cunique_ptr() {
        ptr = new T;
    }
    ~cunique_ptr() {
        delete ptr;
    }

    // You can only get a non-const pointer to the internals from a non-const object
    T* get() { return ptr; }

    // The const member function carries over the const promise to access to its internals
    const T* get() const { return ptr; }
};

void test() {
    cunique_ptr<A> a;
    a.get()->nonConst();

    const cunique_ptr<A> ca;
    //ca.get()->nonConst(); //X fails: cannot call non-const member functions from const object
    ca.get()->Const();
}

However, it seems the library designers elected against that type of protection and let the const promise be kind of shallow as it were.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.