Why does std::unique_ptr not have a const get method?

Viewed 4654

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.

5 Answers

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 )

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.

Related