Consider the following scenario: There is a class CDriver that is in charge of enumerating all the attached output devices (represented by the COutput class). The code for that may look something like this:
class COutput
{
// COutput stuff
};
class CDriver
{
public:
CDriver(); // enumerate outputs and store in m_outputs
// some other methods
private:
std::vector<COutput> m_outputs;
};
Now CDriver should be able to grant the user access to the enumerated COutputs.
The first method of achieving this is to return a pointer:
const COutput* GetOutput(unsigned int idx) const
{
return idx < m_outputs.size() ? &m_outputs[idx] : nullptr;
}
The way I see it, this method presents the problem that if the pointer is stored by the user and it persists after the CDriver object has been destroyed, it is now a dangling pointer. This is due to the fact that the pointee (COutput object) has been destroyed during the destructor of the CDriver object.
The second way of doing this would be to return by reference:
const COutput& GetOutput(unsigned int idx) const
{
return idx < m_outputs.size() ? &m_outputs[idx] : m_invalidOutput;
}
Here the same problems apply as in the approach with pointers. Furthermore it has the additional caveat that no real invalid object can be returned. If a nullptr is returned as a return pointer, it is obvious that it is "invalid". There is, however, no equivalent to nullptr when it comes to references.
Moving on to approach number three. Return by value.
COutput GetOutput(unsigned int idx) const
{
return idx < m_outputs.size() ? &m_outputs[idx] : m_invalidOutput;
}
Here, the user doesn't have to worry about the lifetime of the returned object. However, the COutput object has to be copied and there is, similary to the reference approach, no intuitive way to check for errors.
I could go on...
For example, the COutput objects could be allocated on the heap and stored in std::shared_ptrs and returned as such. This, however would make the code very verbose.
Is there any way to solve this problem intuitively and without introducing unnecessary code verbosity?