tags:

views:

204

answers:

2

I'm in the process of designing several classes that need to support operators !=, >, <=, and >=. These operators will be implemented in terms of operators == and <.

At this stage, I need to make a choice between inheritance¹ and forcing my consumers to use std::rel_ops² "manually".

[1] Inheritance (possible implementation):

template<class T> class RelationalOperatorsImpl
{
    protected:
    RelationalOperatorsImpl() {}
    ~RelationalOperatorsImpl() {}

    friend bool operator!=(const T& lhs, const T& rhs) {return !(lhs == rhs);}
    friend bool operator>(const T& lhs, const T& rhs) {return (rhs < lhs);}
    friend bool operator<=(const T& lhs, const T& rhs) {return !(rhs < lhs);}
    friend bool operator>=(const T& lhs, const T& rhs) {return !(lhs < rhs);}
};

template<typename T> class Foo : RelationalOperatorsImpl< Foo<T> >
{
    public:
    explicit Foo(const T& value) : m_Value(value) {}

    friend bool operator==(const Foo& lhs, const Foo& rhs) {return (lhs.m_Value == rhs.m_Value);}
    friend bool operator<(const Foo& lhs, const Foo& rhs) {return (lhs.m_Value < rhs.m_Value);}

    private:
    T m_Value;
};

[2] std::rel_ops glue:

template<typename T> class Foo
{
    public:
    explicit Foo(const T& value) : m_Value(value) {}

    friend bool operator==(const Foo& lhs, const Foo& rhs) {return (lhs.m_Value == rhs.m_Value);}
    friend bool operator<(const Foo& lhs, const Foo& rhs) {return (lhs.m_Value < rhs.m_Value);}

    private:
    T m_Value;
};

void Consumer()
{
    using namespace std::rel_ops;

    //Operators !=, >, >=, and <= will be instantiated for Foo<T> (in this case) on demand.
}

I'm basically trying to avoid code repetition. Any thoughts as to which method "feels" better?

+5  A: 

Have you considered using boost, and having your class inherit from boost::less_than_comparable<T> and boost::equality_comparable<T>? It is akin to your first suggestion, with some pros and cons. Pros: avoids code duplication; Cons: creates a dependency on boost.

Since boost is a very common C++ library (if you don't use it already, you should seriously consider start using it), the con factor is dimmed.

Juliano
That's an excellent suggestion. Boost never ceases to surprise me. I'd like to hear from others before I accept an answer, though. ;)
+1  A: 

I think std::rel_ops is quite nice, but there's one thing to consider first: std::rel_ops provides operators as template functions that accept two parameters of the same type. Because most conversions (including e.g. arithmetic promotions and user-defined conversions) are not performed when template argument deduction occurs, this means that you would not be able to use any of these additional operators (e.g. !=) with such conversions.

E.g. if you have a class MyInt that attempts to behave like a regular integer, you might have written conversion functions/constructors or templated operators so that you can do

MyInt x, y;

x < 5;
9 == x;

However,

x > 5;
30 <= x;

won't work (with std::rel_ops) because the two arguments are of different types, so template argument deduction will fail.

j_random_hacker