Const correctness of operator* in std::unique_ptr

Viewed 190

Why can 'operator*' member function of std::unique_ptr be marked const (https://en.cppreference.com/w/cpp/memory/unique_ptr/operator*) while functions like 'front()', 'back()', 'operator[]' etc in std::vector not be marked const? Both are returning non-const references to the resources they are managing.

The core problem is I can't understand the 1st part i.e. why we're able to mark 'operator*' as const i.e. how can the return type be 'T&' when the '*this' pointer in that function would be a const pointer (due to the function being marked const)?

2 Answers

The difference lies in the definition of a container.

A std::vector is considered a container. This means that the objects managed by a std::vector are considered parts of the vector.

The references returned from front(), back() and operator[] must be const if the vector is const. Modifying those objects will modify the vector.

A std::unique_ptr, however, is not a container. The object it manages is not considered to be part of the std::unique_ptr. This means that modifying the managed object is not considered modifying the pointer.

Therefore, using operator* on a pointer type will never change the pointer itself, and therefore the operation can always be considered const.


std::optional may provide some helpful context to this difference.

Despite having a syntax that resembles a pointer, it is effectively a container (with max_size of 1). Because it contains its managed object, its operator* must obey the same const rules that a container does.

Drew Dormann's excellent answer above resolves the core confusion that I had. Adding a summary here (since it is too long to be posted as a comment) for complete clarity.

Doubt 1 : How are we able to use T& as the return type even though std::unique_ptr::operator*() is marked a const function?

Answer 1 : std::unique_ptr just stores the pointer to the data being managed (the data itself is not stored within the class, but rather on the heap). We're able to mark its operator*() function as const because we're returning a non-const reference to the actual data being stored & since that data itself is not stored inside the class, the 'const' qualifier just makes the pointer to the data const (i.e. we have a const pointer) but the data that this const pointer points to is still non-const and we can bind non-const references to it.

Doubt 2 : The same thing is true for std::vector (i.e. the data managed by it is not stored within the class, but on the heap instead) but then why we don't similarly mark functions like std::vector::front(), std::vector::back() etc as const?

Answer 2 : This is due to the semantics associated with container classes in C++. Quoting Drew from above - std::vector models properties of an array, while a std::unique_ptr models properties of a pointer. So we kind of act as if the data managed by std::vector resides inside the class. Note that std::optional is different from std::unique_ptr / std::vector as the data being managed is actually stored within the class.

Related