3

I have an AVL tree which uses templates and assumes that the node objects are comparable, so it compares them directly, rather than comparing some kind of key associated with the objects:

void insert( const Comparable & x, AvlNode * & t )
{
    if( t == nullptr )
        t = new AvlNode( x, nullptr, nullptr );
    else if( x < t->element )
        insert( x, t->left );
    else if( t->element < x )
        insert( x, t->right );

    balance( t );
}

In order for this to work, I implemented an overloaded < operator in my class, which uses a member variable of the class to compare the two objects:

bool operator <(const myClass & myObject) const
{
    return myVariable < myObject.myVariable;
}

This works perfectly when I create an AVL tree of objects:

AvlTree<myClass> myTree;

However, it does not work when I create an AVL tree of pointers to objects:

AvlTree<myClass*> myTree;

The comparisons inside the tree seem to compare the addresses of the pointers, rather than the member variables. I tried implementing a similar overloaded < operator in my class for pointers:

bool operator <(const myClass *& myObject) const
{
    return myVariable < myObject->myVariable;
}

But the comparisons ignore my overloaded operator and still use the addresses of the pointers. Is there any way to force the comparisons to use my operator, just as they do with normal objects?

CuriouS
  • 129
  • 1
  • 9
  • Do not (only) overload `<`, pass a comparator class as another template parameter to the tree template. See std::map for details. – n. m. could be an AI Oct 30 '14 at 13:49
  • I suggest you look at this one http://stackoverflow.com/questions/301330/determine-if-type-is-a-pointer-in-a-template-function – Lan Pac Oct 30 '14 at 13:54

1 Answers1

1

It's possible, but somewhat non-trivial.

The usual way to do the job is to pass a function that the tree will use to compare what's stored. You can provide a default for this function, typically using std::less<T> as the default, but allowing the user to pass something else if they choose to do so. Of course, you'll need to rewrite your code to use this instead of using < directly:

template <class T, class Less=std::less<T>>
class AvlTree {

public:

    void insert( const Comparable & x, AvlNode * & t )
    {
        if( t == nullptr )
            t = new AvlNode( x, nullptr, nullptr );
        else if( Less(x, t->element) )
            insert( x, t->left );
        else if( Less(t->element, x) )
            insert( x, t->right );

        balance( t );
    }

    // ...
};

...then for a tree of pointers, you'd specify a suitable way to do the comparison:

template <class T>
struct LessPtr { 
    bool operator()(T *a, T *b) { 
        return *a < *b;
    }
};

...and pass an instantiation of that when you instantiate your tree:

AvlTree<MyClass *, LessPtr<MyClass>> my_tree;

Now your tree should compare the pointed-to objects rather than the pointers themselves.

There are, of course, other ways to do this. At the risk of doing the wrong thing in some cases, you could (for example) use template specialization to define a specialization for pointers that compared the pointee objects instead of the pointers themselves. This could (probably would) still run into problems if a user tried to create a tree of MyObject ** though. At least to me, the potential for problems here looks serious enough that I'd advise against it though.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111