Search code examples
c++memory-managementunique-ptrownership

Returning the raw pointer from a list of unique pointers


I have a class (A) that will own the memory for a set of instances of another class (B). A will store its instances of B as a set of std::unique_ptr<B>.

I want A to be able to return a reference to a set of raw pointers to those B objects, (i.e. I don't want any transfer of ownership). What is the best way to do this?

class A
{
private:
    std::set<std::unique_ptr<B>> _Bs;
public:
    std::set<B*>& getBs() { ???? };
}

I could create a new vector:

class A
{
private:
    std::set<std::unique_ptr<B>> _Bs;
    std::set<B*>                 _rawBs;
public:
    std::set<B*>& getBs() { return _rawBs; };
}
std::set<B*>
A::A()
{
    std::transform(_Bs.begin(), 
                   _Bs.end(), 
                   std::inserter(_rawBs, _rawBs.begin()), 
                   [](auto uniq_ptr) { uniq_ptr.get(); }
    
}

But it seems a bit janky to store these pointers twice...


Solution

  • You should add a member function:

    const std::set<std::unique_ptr<B>>& getBs() {
        return m_bs;
    }
    

    There are a few things to note:

    • Unfortunately you cannot return something like std::span and have to return a reference to a const std::set. This leaks more implementation details, but is necessary because sets aren't contiguous containers.

    • You can still return a std::vector<B*> that contains a copy of all the pointers, but this would be more expensive.

    • A const std::set only lets you access the elements inside as const std::unique_ptr, so no ownership is being transferred, despite exposing the std::unique_ptr. Transfer of ownership would also require the ability to modify the std::unique_ptr.

    • You should not use the name _Bs for a data member because an underscore followed by a capital letter makes for a reserved identifier. See What are the rules about using an underscore in a C++ identifier?. The most popular naming scheme for members (if you give them a prefix or suffix) is the prefix m_.