I have the following object:
class Container {
public:
std::vector<std::unique_ptr<Item>>& items() { return m_items; }
private:
std::vector<std::unique_ptr<Item>> m_items;
};
I'd like to use const to make this whole object immutable but still provide access to items(), even though it's using pointers internally. E.g. declare void doSomething(const Container& container) and be sure that the code doesn't accidentally change its contents.
How can I write const auto& items() const { ... } to allow read-only access to the vector of items without the caller being able to modify any of the items, the pointers or the vector?
Attempt #1: The first step is...
const std::vector<std::unique_ptr<Item>>& items() const { return m_items; }
...
void doSomething(const Container& container)
{
// Good: const container, can't do this
container.items().clear();
// Good: const unique_ptr, can't do this
container.items()[0].reset();
// Bad: non-const items
*container.items()[0] = ...;
}
Attempt #2: What I really want is this, but it's UB because there's no guarantee that the const type has the same memory layout (and maybe other reasons?).
const std::vector<std::unique_ptr<const Item>>& items() const {
return reinterpret_cast<const std::vector<std::unique_ptr<const Item>>&>(m_items);
// ^_________ change unique_ptr type
}
Attempt #3: A rather terrible alternative is to construct and return a temporary vector. Equally bad would be to cache and maintain this duplicate vector.
std::vector<const Item*> items() const {
return {m_items.begin(), m_items.end()};
}
Attempt #4: Implement something like std::span, but have the iterator only return const references. In this case it would need to unwrap the std::unique_ptr. This would work, but it's a fair bit of effort for just one use-case. Maybe well into the realm of over-engineering too?
There might be some unexpected side effects for code expecting certain types and not using auto when iterating, but I'm ok with that.
[EDIT] This is vaguely similar to the concept of a deep copy. Following the term lead here: