Return an immutable vector of unique_ptr

112 Views Asked by At

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:

0

There are 0 best solutions below