The best way to bind the implementation of some function (predicate in this case) with the type of the objects to which you hold pointers is indeed to use polymorphism. That is, if you need to print the object, for example, you could make the print function virtual in the base class.
However, since you have a binary function/predicate, your situation is more complex than that. And this is not just technical problem. If you need to be able to compare objects of all types in some sub-tree from the type hierarchy with all others in this sub-tree, then look at hamster's solution, because it is as good as it gets. Not that this solution is bad, but if you have many types, then it can become hard to maintain.
On the other hand, if you need to compare only likes with likes - that is, only objects with the same dynamic type, then you probably have homogeneous container. What I mean is, your tree than should be designed to store only objects of the same type. Different trees can store different types of objects, but a single tree is restricted to specific type.
To the point - in this case you can use templates and create lightweight type-aware front-end for the container that derives from some type-oblivious back-end. The back-end carries all the computational and processing weight behind the operations, while the front-end only provides some type intelligence. The split is necessary in order to avoid template code bloat - that is machine code overhead. On the other hand, if you don't care much about memory waste, you can simply make your container generic (no back-end) and forget about polymorphism and what have you.
I have created dummy containers in this example that do nothing but sort and print their data. I have tried to illustrate the use of both polymorphism and type-awareness at once. It is not pretty, and some of the C++ limitations make it even uglier, but it should work. Again, this is all assuming that your container is homogeneous. Otherwise, look at the hamster's solution.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
|
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
template<typename Base>
class TypeObliviousContainerBase {
public:
template <typename D>
TypeObliviousContainerBase(D*,
typename D::derived_t data[],
size_t size)
: m_data(size), compare_obj_ptr(D::compare_obj)
{
for (int i = 0; i < (int)size; ++i)
{
m_data[i] = data + i;
}
}
void sort()
{
std::sort(m_data.begin(), m_data.end(), compare_obj_ptr);
}
void print()
{
for_each(m_data.begin(), m_data.end(), std::mem_fun(&Base::print));
std::cout << std::endl;
}
private:
std::vector<Base*> m_data;
bool (* const compare_obj_ptr)(Base*, Base*);
};
template <typename Derived, typename Base>
class TypeAwareContainer : public TypeObliviousContainerBase<Base> {
public:
typedef Derived derived_t;
typedef Base base_t;
template <size_t N>
TypeAwareContainer(Derived (& data)[N])
: TypeObliviousContainerBase<Base>(this, data, N) {}
private:
static bool compare_obj(Base* left, Base* right)
{
return static_cast<Derived*>(left)->
compare(*static_cast<Derived*>(right));
}
friend class TypeObliviousContainerBase<Base>;
};
//****************************************************
// Here are the types that are held in the containers.
//****************************************************
struct AbstractBase
{
virtual void print() const = 0;
};
struct DerivedSortAscending : AbstractBase
{
int value;
DerivedSortAscending(int x) : value(x) {};
bool compare(const DerivedSortAscending& other) const
{
return value < other.value;
}
virtual void print() const
{
std::cout << value << " < ";
}
};
struct DerivedSortDescending : AbstractBase
{
int value;
DerivedSortDescending(int x) : value(x) {};
bool compare(const DerivedSortDescending& other) const
{
return value > other.value;
}
virtual void print() const
{
std::cout << value << " > ";
}
};
//********************
// Some demonstration.
//********************
template <typename Derived, typename Base>
void test()
{
Derived data[] = {Derived(2), Derived(1), Derived(3)};
TypeAwareContainer<Derived, Base> c(data);
c.sort();
c.print();
}
int main()
{
test<DerivedSortAscending, AbstractBase>();
test<DerivedSortDescending, AbstractBase>();
}
|
There is another option - to make the binary operation/predicate virtual and to use dynamic_cast, but this approach provides fewer static type checks, and I am not comfortable with it. It is easier to implement, but more error prone.
Regards