Consider this (simplified) class hierarchy in a c++98 source (godbolt):
class Shape
{
public:
virtual class CurvilinearShape as_curvilinear_shape() const =0;
};
class CurvilinearShape : public Shape
{
// ...
public:
virtual CurvilinearShape as_curvilinear_shape() const
{
return *this;
}
};
class PolygonShape : public Shape
{
// ...
public:
virtual CurvilinearShape as_curvilinear_shape() const
{
CurvilinearShape curv_shape;
// Convert...
return curv_shape;
}
};
A PolygonShape
can be easily converted to a CurvilinearShape
.
The reason that led to those two distinct classes is that a PolygonShape
has a simplified description that can be processed by simpler machines,
while more advanced ones can process the more generic CurvilinearShape
descriptor:
void work_curvilinear_shape(CurvilinearShape const&);
In this case it's very handy to process a sequence of base Shape
items without worrying about the actual descriptor used, the virtual
member function as_curvilinear_shape()
was introduced just for that:
#include <vector>
int main()
{
std::vector<Shape*> shapes;
for(std::vector<Shape*>::const_iterator it=shapes.begin(); it!=shapes.end(); ++it)
{
work_curvilinear_shape( (*it)->as_curvilinear_shape() );
}
}
This works, but has the problem of the unnecessary copy construction that
takes place each time CurvilinearShape::as_curvilinear_shape()
is called
(incidentally the majority of the times).
Although this seems a fairly common use case I cannot find a name
for it, do you know it?
I'm searching for a better approach that avoids the unnecessary copy in case
the item is already a CurvilinearShape
instance, maybe one of you can put me on the right track?
A (somewhat cumbersome) solution is to give as_curvilinear_shape
the following signature:
const CurvilinearShape &as_curvilinear_shape(CurvilinearShape &storage) const;
If it's already the right shape, return *this
and leave storage
unchanged. If a conversion is needed, assign the conversion result to storage
and return a reference to storage
.
Or you might want to create a helper class and return it:
struct CurvilinearShapeOrRef
{
CurvilinearShape storage; // or `std::optional`/`boost::optional`.
const CurvilinearShape *ptr_override = nullptr; // C++98 can't do this, make a constructor.
const CurvilinearShape &get() const {return ptr_override ? *ptr_override : storage;}
};