1

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...

9
  • 6
    How about returning cosnt std::set<std::unique_ptr<B>> &? This isn't an ownership transfer. Commented Apr 18, 2024 at 15:32
  • I'm surprised std::set of std::unique_ptr even works. What does the comparison operator do? Commented Apr 18, 2024 at 15:39
  • @MarkRansom Probably the same thing std::less does for raw pointers. Commented Apr 18, 2024 at 15:42
  • I would just return a const & for the existing set. If the user wants a copy, they can make one themselves. As a side note, I don't even understand the usefulness of a set here... Commented Apr 18, 2024 at 16:25
  • @MarkRansom en.cppreference.com/w/cpp/memory/unique_ptr/operator_cmp Commented Apr 18, 2024 at 17:51

2 Answers 2

0

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_.

Sign up to request clarification or add additional context in comments.

Comments

0

I want A to be able to return a reference to a set of raw pointers to those B objects

That's a bad plan. What do you do if a caller modifies that set?

You could instead return a view of pointers, by value.

class A
{
private:
    std::set<std::unique_ptr<B>> Bs;
public:
    auto getBs() const { 
        return Bs | std::views::transform([](auto & ptr){ return ptr.get(); }); 
    };
};

If your std::unique_ptr<B>s are guaranteed to be non-null, you could instead return a view of references by swapping the ptr.get() for *ptr.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.