c++boost.gif (8819 bytes)Header boost/operators.hpp

Header boost/operators.hpp supplies (in namespace boost) several sets of templates:

These templates define many global operators in terms of a minimal number of fundamental operators.

Arithmetic Operators

If, for example, you declare a class like this:

class MyInt : boost::operators<MyInt>
{
    bool operator<(const MyInt& x) const; 
    bool operator==(const MyInt& x) const;
    MyInt& operator+=(const MyInt& x);    
    MyInt& operator-=(const MyInt& x);    
    MyInt& operator*=(const MyInt& x);    
    MyInt& operator/=(const MyInt& x);    
    MyInt& operator%=(const MyInt& x);    
    MyInt& operator|=(const MyInt& x);    
    MyInt& operator&=(const MyInt& x);    
    MyInt& operator^=(const MyInt& x);    
    MyInt& operator++();    
    MyInt& operator--();    
};

then the operators<> template adds more than a dozen additional operators, such as operator>, <=, >=, and +.  Two-argument forms of the templates are also provided to allow interaction with other types.

Dave Abrahams started the library and contributed the arithmetic operators in boost/operators.hpp.
Jeremy Siek contributed the dereference operators and iterator helpers in boost/operators.hpp.
Aleksey Gurtovoy contributed the code to support base class chaining while remaining backward-compatible with old versions of the library.
Beman Dawes contributed test_operators.cpp.

Rationale

Overloaded operators for class types typically occur in groups. If you can write x + y, you probably also want to be able to write x += y. If you can write x < y, you also want x > y, x >= y, and x <= y. Moreover, unless your class has really surprising behavior, some of these related operators can be defined in terms of others (e.g. x >= y <=> !(x < y)). Replicating this boilerplate for multiple classes is both tedious and error-prone. The boost/operators.hpp templates help by generating operators for you at namespace scope based on other operators you've defined in your class.

Two-Argument Template Forms

The arguments to a binary operator commonly have identical types, but it is not unusual to want to define operators which combine different types. For example, one might want to multiply a mathematical vector by a scalar. The two-argument template forms of the arithmetic operator templates are supplied for this purpose. When applying the two-argument form of a template, the desired return type of the operators typically determines which of the two types in question should be derived from the operator template. For example, if the result of T + U is of type T, then T (not U) should be derived from addable<T,U>. The comparison templates less_than_comparable<> and equality_comparable<> are exceptions to this guideline, since the return type of the operators they define is bool.

On compilers which do not support partial specialization, the two-argument forms must be specified by using the names shown below with the trailing '2'. The single-argument forms with the trailing '1' are provided for symmetry and to enable certain applications of the base class chaining technique.

Arithmetic operators table

The requirements for the types used to instantiate operator templates are specified in terms of expressions which must be valid and by the return type of the expression. In the following table t and t1 are values of type T, and u is a value of type U. Every template in the library other than operators<> and operators2<> has an additional optional template parameter B which is not shown in the table, but is explained below

template template will supply Requirements
operators<T> All the other <T> templates in this table. All the <T> requirements in this table.
operators<T,U>
operators2<T,U>
All the other <T,U> templates in this table, plus incrementable<T> and decrementable<T>. All the <T,U> requirements in this table*, plus incrementable<T> and decrementable<T>.
less_than_comparable<T>
less_than_comparable1<T>
bool operator>(const T&, const T&) 
bool operator<=(const T&, const T&)
bool operator>=(const T&, const T&)
t<t1. Return convertible to bool
less_than_comparable<T,U>
less_than_comparable2<T,U>
bool operator<=(const T&, const U&)
bool operator>=(const T&, const U&)
bool operator>(const U&, const T&) 
bool operator<(const U&, const T&) 
bool operator<=(const U&, const T&)
bool operator>=(const U&, const T&)
t<u. Return convertible to bool
t>u. Return convertible to bool
equality_comparable<T>
equality_comparable1<T>
bool operator!=(const T&, const T&) t==t1. Return convertible to bool
equality_comparable<T,U>
equality_comparable2<T,U>
friend bool operator==(const U&, const T&)
friend bool operator!=(const U&, const T&)
friend bool operator!=( const T&, const U&)
t==u. Return convertible to bool
addable<T>
addable1<T>
T operator+(T, const T&) t+=t1. Return convertible to T
addable<T,U>
addable2<T,U>
T operator+(T, const U&)
T operator+(const U&, T )
t+=u. Return convertible to T
subtractable<T>
subtractable1<T>
T operator-(T, const T&) t-=t1. Return convertible to T
subtractable<T,U>
subtractable2<T,U>
T operator-(T, const U&) t-=u. Return convertible to T
multipliable<T>
multipliable1<T>
T operator*(T, const T&) t*=t1. Return convertible to T
multipliable<T,U>
multipliable2<T,U>
T operator*(T, const U&)
T operator*(const U&, T )
t*=u. Return convertible to T
dividable<T>
dividable1<T>
T operator/(T, const T&) t/=t1. Return convertible to T
dividable<T,U>
dividable2<T,U>
T operator/(T, const U&) t/=u. Return convertible to T
modable<T>
modable1<T>
T operator%(T, const T&) t%=t1. Return convertible to T
modable<T,U>
modable2<T,U>
T operator%(T, const U&) t%=u. Return convertible to T
orable<T>
orable1<T>
T operator|(T, const T&) t|=t1. Return convertible to T
orable<T,U>
orable2<T,U>
T operator|(T, const U&)
T operator|(const U&, T )
t|=u. Return convertible to T
andable<T>
andable1<T>
T operator&(T, const T&) t&=t1. Return convertible to T
andable<T,U>
andable2<T,U>
T operator&(T, const U&)
T operator&(const U&, T)
t&=u. Return convertible to T
xorable<T>
xorable1<T>
T operator^(T, const T&) t^=t1. Return convertible to T
xorable<T,U>
xorable2<T,U>
T operator^(T, const U&)
T operator^(const U&, T )
t^=u. Return convertible to T
incrementable<T>
incrementable1<T>
T operator++(T& x, int) T temp(x); ++x; return temp;
Return convertible to T
decrementable<T>
decrementable1<T>
T operator--(T& x, int) T temp(x); --x; return temp;
Return convertible to T

Portability Note: many compilers (e.g. MSVC6.3, GCC 2.95.2) will not enforce the requirements in this table unless the operations which depend on them are actually used. This is not standard-conforming behavior. If you are trying to write portable code it is important not to rely on this bug. In particular, it would be convenient to derive all your classes which need binary operators from the operators<> and operators2<> templates, regardless of whether they implement all the requirements in the table. Even if this works with your compiler today, it may not work tomorrow.

Base Class Chaining and Object Size

Every template listed in the table except operators<> and operators2<> has an additional optional template parameter B.  If supplied, B must be a class type; the resulting class will be publicly derived from B. This can be used to avoid the object size bloat commonly associated with multiple empty base classes (see the note for users of older versions below for more details). To provide support for several groups of operators, use the additional parameter to chain operator templates into a single-base class hierarchy, as in the following example.

Caveat: to chain to a base class which is not a boost operator template when using the single-argument form of a boost operator template, you must specify the operator template with the trailing '1' in its name. Otherwise the library will assume you mean to define a binary operation combining the class you intend to use as a base class and the class you're deriving.

Borland users: even single-inheritance seems to cause an increase in object size in some cases. If you are not defining a template, you may get better object-size performance by avoiding derivation altogether, and instead explicitly instantiating the operator template as follows:

    class myclass // lose the inheritance...
    {
        //...
    };
    // explicitly instantiate the operators I need.
    template class less_than_comparable<myclass>;
    template class equality_comparable<myclass>;
    template class incrementable<myclass>;
    template class decrementable<myclass>;
    template class addable<myclass,long>;
    template class subtractable<myclass,long>;

Usage example

template <class T>
class point    // note: private inheritance is OK here!
    : boost::addable< point<T>          // point + point
    , boost::subtractable< point<T>     // point - point
    , boost::dividable2< point<T>, T    // point / T
    , boost::multipliable2< point<T>, T // point * T, T * point
      > > > >
{
public:
    point(T, T);
    T x() const;
    T y() const;

    point operator+=(const point&);
    // point operator+(point, const point&) automatically
    // generated by addable.

    point operator-=(const point&);
    // point operator-(point, const point&) automatically
    // generated by subtractable.

    point operator*=(T);
    // point operator*(point, const T&) and
    // point operator*(const T&, point) auto-generated
    // by multipliable.

    point operator/=(T);
    // point operator/(point, const T&) auto-generated
    // by dividable.
private:
    T x_;
    T y_;
};

// now use the point<> class:

template <class T>
T length(const point<T> p)
{
    return sqrt(p.x()*p.x() + p.y()*p.y());
}

const point<float> right(0, 1);
const point<float> up(1, 0);
const point<float> pi_over_4 = up + right;
const point<float> pi_over_4_normalized = pi_over_4 / length(pi_over_4);

Arithmetic operators demonstration and test program

The operators_test.cpp program demonstrates the use of the arithmetic operator templates, and can also be used to verify correct operation.

The test program has been compiled and run successfully with: 

Dereference operators and iterator helpers

The iterator helper templates ease the task of creating a custom iterator. Similar to arithmetic types, a complete iterator has many operators that are "redundant" and can be implemented in terms of the core set of operators.

The dereference operators were motivated by the iterator helpers, but are often useful in non-iterator contexts as well. Many of the redundant iterator operators are also arithmetic operators, so the iterator helper classes borrow many of the operators defined above. In fact, only two new operators need to be defined! (the pointer-to-member operator-> and the subscript operator[]).

Notation

T is the user-defined type for which the operations are being supplied.
V is the type which the resulting dereferenceable type "points to", or the value_type of the custom iterator.
D is the type used to index the resulting indexable type or the difference_type of the custom iterator.
P is a type which can be dereferenced to access V, or the pointer type of the custom iterator.
R is the type returned by indexing the indexable type or the reference type of the custom iterator.
i is short for static_cast<const T&>(*this), where this is a pointer to the helper class.
Another words, i should be an object of the custom iterator type.
x,x1,x2 are objects of type T.
n is an object of type D.

The requirements for the types used to instantiate the dereference operators and iterator helpers are specified in terms of expressions which must be valid and their return type. 

Dereference operators

The dereference operator templates in this table all accept an optional template parameter (not shown) to be used for base class chaining.
template template will supply Requirements
dereferenceable<T,P> P operator->() const (&*i.). Return convertible to P.
indexable<T,D,R> R operator[](D n) const *(i + n). Return of type R.

Iterator helpers

There are three separate iterator helper classes, each for a different category of iterator. Here is a summary of the core set of operators that the custom iterator must define, and the extra operators that are created by the helper classes. For convenience, the helper classes also fill in all of the typedef's required of iterators by the C++ standard (iterator_category, value_type, etc.).

template template will supply Requirements
forward_iterator_helper
<T,V,D,P,R>
bool operator!=(const T& x1, const T& x2)
T operator++(T& x, int)
V* operator->() const
x1==x2. Return convertible to bool
T temp(x); ++x; return temp;
(&*i.). Return convertible to V*.
bidirectional_iterator_helper
<T,V,D,P,R>
Same as above, plus
T operator--(T& x, int)
Same as above, plus
T temp(x); --x; return temp;
random_access_iterator_helper
<T,V,D,P,R>
Same as above, plus
T operator+(T x, const D&)
T operator+(const D& n, T x)
T operator-(T x, const D& n)
R operator[](D n) const
bool operator>(const T& x1, const T& x2) 
bool operator<=(const T& x1, const T& x2)
bool operator>=(const T& x1, const T& x2)
Same as above, plus
x+=n. Return convertible to T
x-=n. Return convertible to T
x1<x2. Return convertible to bool
And to satisfy RandomAccessIterator:
x1-x2. Return convertible to D

Iterator demonstration and test program

The iterators_test.cpp program demonstrates the use of the iterator templates, and can also be used to verify correct operation. The following is the custom iterator defined in the test program. It demonstrates a correct (though trivial) implementation of the core operations that must be defined in order for the iterator helpers to "fill in" the rest of the iterator operations.

template <class T, class R, class P>
struct test_iter
  : public boost::random_access_iterator_helper<
     test_iter<T,R,P>, T, std::ptrdiff_t, P, R>
{
  typedef test_iter self;
  typedef R Reference;
  typedef std::ptrdiff_t Distance;

public:
  test_iter(T* i) : _i(i) { }
  test_iter(const self& x) : _i(x._i) { }
  self& operator=(const self& x) { _i = x._i; return *this; }
  Reference operator*() const { return *_i; }
  self& operator++() { ++_i; return *this; }
  self& operator--() { --_i; return *this; }
  self& operator+=(Distance n) { _i += n; return *this; }
  self& operator-=(Distance n) { _i -= n; return *this; }
  bool operator==(const self& x) const { return _i == x._i; }
  bool operator<(const self& x) const { return _i < x._i; }
  friend Distance operator-(const self& x, const self& y) {
    return x._i - y._i; 
  }
protected:
  T* _i;
};

It has been compiled and run successfully with:

Jeremy Siek contributed the iterator operators and helpers.  He also contributed iterators_test.cpp


Note for users of older versions

The changes in the library interface and recommended usage were motivated by some practical issues described below. The new version of the library is still backward-compatible with the former one (so you're not forced change any existing code), but the old usage is deprecated. Though it was arguably simpler and more intuitive than using base class chaining, it has been discovered that the old practice of deriving from multiple operator templates can cause the resulting classes to be much larger than they should be. Most modern C++ compilers significantly bloat the size of classes derived from multiple empty base classes, even though the base classes themselves have no state. For instance, the size of point<int> from the example above was 12-24 bytes on various compilers for the Win32 platform, instead of the expected 8 bytes.

Strictly speaking, it was not the library's fault - the language rules allow the compiler to apply the empty base class optimization in that situation. In principle an arbitrary number of empty base classes can be allocated at the same offset, provided that none of them have a common ancestor (see section 10.5 [class.derived], par. 5 of the standard). But the language definition also doesn't require implementations to do the optimization, and few if any of today's compilers implement it when multiple inheritance is involved. What's worse, it is very unlikely that implementors will adopt it as a future enhancement to existing compilers, because it would break binary compatibility between code generated by two different versions of the same compiler. As Matt Austern said, "One of the few times when you have the freedom to do this sort of thing is when you're targeting a new architecture...". On the other hand, many common compilers will use the empty base optimization for single inheritance hierarchies.

Given the importance of the issue for the users of the library (which aims to be useful for writing light-weight classes like MyInt or point<>), and the forces described above, we decided to change the library interface so that the object size bloat could be eliminated even on compilers that support only the simplest form of the empty base class optimization. The current library interface is the result of those changes. Though the new usage is a bit more complicated than the old one, we think it's worth it to make the library more useful in real world. Alexy Gurtovoy contributed the code which supports the new usage idiom while allowing the library remain backward-compatible.


Revised 10 Feb 2001

© Copyright David Abrahams and Beman Dawes 1999-2000. Permission to copy, use, modify, sell and distribute this document is granted provided this copyright notice appears in all copies. This document is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.