Search code examples
c++constantssmart-pointersprivate-memberscontainer-data-type

How to return a private pointer to a list of pointers as const?


I have a pointer to a list of pointers, as a private variable. I also have a getter that returns the pointer to the list. I need to protect it from changes.

I couldn't find how to use reinterpret_cast or const_cast on this.

class typeA{
    shared_ptr<list<shared_ptr<typeB>>> l;
public:
   shared_ptr<list<shared_ptr<const typeB>>> getList(){return (l);};
};

The compiler returns:

   error: could not convert ‘((typeA*)this)->typeA::x’ from ‘std::shared_ptr<std::__cxx11::list<std::shared_ptr<typeB> > >’ to ‘std::shared_ptr<std::__cxx11::list<std::shared_ptr<const typeB> > >’|
||=== Build failed: 1 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|

It seems as const shared_ptr<list<shared_ptr<typeB>>> and shared_ptr<const list<shared_ptr<typeB>>> work fine.

Is it possible to do return l as a complete const, like:

const shared_ptr<const list<shared_ptr<const typeB>>>

or at least like:

shared_ptr<list<shared_ptr<const typeB>>> 

?

References instead of pointers is not an option. To declare l as shared_ptr<list<shared_ptr<const typeB>>> also is not a wanted solution.

EDIT: no 'int' anymore.

It seems as it is not possible exactly what I wanted, but the suggested solutions are good. Yes, copying pointers is acceptable.

My bad i didn't put typeB immediately. I am aware of some advantages of references over pointers, but I hoped there is some similar solution.


Solution

  • You can create a new list of const int's from your original list and return that:

    std::shared_ptr<std::list<std::shared_ptr<const int>>> getList(){
        return std::make_shared<std::list<std::shared_ptr<const int>>>(l->begin(), l->end());
    }
    

    If you want to prevent people from making changes to the returned list, make it const too:

    std::shared_ptr<const std::list<std::shared_ptr<const T>>> getList(){
        return std::make_shared<const std::list<std::shared_ptr<const T>>>(l->cbegin(), l->cend());
    }
    

    The shared pointer returned by this function does not point to the original list but to the newly created list.

    An alternative may be to provide iterators that, when dereferenced, returns const T& (where T is the type you actually store). That way there will be no need to copy the whole list every time you want to go though it. Example:

    #include <iostream>
    #include <list>
    #include <memory>
    
    struct example {
        int data;
        example(int x) : data(x) {}
    };
    
    template <class T>
    class typeA {
        std::shared_ptr<std::list<std::shared_ptr<T>>> l = std::make_shared<std::list<std::shared_ptr<T>>>();
    public:
        template< class... Args >
        void add( Args&&... args ) {
            l->emplace_back(std::make_shared<T>(std::forward<Args>(args)...));
        }
    
        // a very basic iterator that can be extended as needed   
        struct const_iterator {
            using uiterator = typename std::list<std::shared_ptr<T>>::const_iterator;
            uiterator lit;
            const_iterator(uiterator init) : lit(init) {}
            const_iterator& operator++() { ++lit; return *this; }
            const T& operator*() const { return *(*lit).get(); }
            bool operator!=(const const_iterator& rhs) const { return lit != rhs.lit; }
        };
    
        const_iterator cbegin() const noexcept { return const_iterator(l->cbegin()); }
        const_iterator cend() const noexcept { return const_iterator(l->cend()); }
        auto begin() const noexcept { return cbegin(); }
        auto end() const noexcept { return cend(); }
    };
    
    int main() {
        typeA<example> apa;
        apa.add(10);
        apa.add(20);
        apa.add(30);
        for(auto& a : apa) {
            // a.data = 5; // error: assignment of member ‘example::data’ in read-only object
            std::cout << a.data << "\n";
        }
    }