Reputation: 10050
I have a bunch of types, PixelMeasure
, PointMeasure
, CentimeterMeasure
and so on, that represent a value with a unit. I would like them to have
Measure
and can operate on it without knowning what specific kind it is. I would also like to be able to put multiple different Measure
s into a container.It seems these are mutually exclusive in C++. For polymorphism, I need to use pointers or references.
I see two options:
shared_ptr
. This gives me the behavior that I want (safe, no raw pointers, but polymorphic dispatch). The downsides are:
shared_ptr<PixelMeasure>
) will share the same underlying pointer. I can still pretend it to have value semantics - if I make the interface immutable, it shouldn't matter.Any ideas?
Upvotes: 8
Views: 2684
Reputation: 9406
You can use type-erase because as Sean Parent puts it, inheritance is the base class of all evil. He also has a presentation Value Semantics and Concept Based Polymorphism which is probably what you want. It is the same idea behind e.g. std::function
.
Basically, you use sub-type polymorphism through inheritance in an internal class to use everything that maps to a concept polymorphically. Here is an example from Type Erasure with Merged Concepts:
class Greeter {
public:
// Constructor: We can stuff anything into a Greeter costume.
template <class T>
Greeter(T data) : self_(std::make_shared<Model<T>>(data)) {}
// External interface: Just forward the call to the wrapped object.
void greet(const std::string &name) const {
self_->greet(name);
}
private:
// The abstract base class is hidden under the covers...
struct Concept {
virtual ~Concept() = default;
virtual void greet(const std::string &) const = 0;
};
// ... and so are the templates.
template <class T>
class Model : public Concept {
public:
Model(T data) : data_(data) {}
virtual void greet(const std::string &name) const override {
// Forward call to user type.
// Requires that T can greet.
data_.greet(name);
}
private:
// The user defined Greeter will be stored here. (by value!)
T data_;
};
// Polymorphic types require dynamic storage.
// Here we store our pointer to the Model that holds the users Greeter.
std::shared_ptr<const Concept> self_;
};
Now, you can put everything into a Greeter object which has a greet method. Other examples are boost::any_iterator or std::function.
You will suffer one memory allocation per Measure value.
Upvotes: 9
Reputation: 67733
You can use a variant type for this: it avoids dynamic allocation, but makes polymorphic dispatch more complicated.
See Boost.Variant, and there's hopefully a standard version on the horizon.
Alternatively, you can write a more specific discriminated union, providing a nice specific polymorphic-style interface
Upvotes: 0
Reputation: 356
You can use a wrapper class with appropriate copy-constructor and a pointer to your Measure as a field. You'll probably need to add clone method to Measure.
class MeasureWrapper
{
public:
MeasureWrapper(const MeasureWrapper &measureToCopy)
{
m_measure = measureToCopy.m_measure->Clone();
}
MeasureWrapper(Measure *measure) : m_measure(measure)
{
}
~MeasureWrapper()
{
delete m_measure;
}
// Wrap Measure interface here and call m_measure methods...
private:
Measure *m_measure;
};
Upvotes: 0