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.
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.
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.
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.
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> |
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> |
bool operator>(const T&, const T&) |
t<t1 . Return convertible to bool |
less_than_comparable<T,U> |
bool operator<=(const T&, const U&) |
t<u . Return convertible to boolt>u . Return convertible to bool |
equality_comparable<T> |
bool operator!=(const T&, const T&) |
t==t1 . Return convertible to bool |
equality_comparable<T,U> |
friend bool operator==(const U&, const T&) |
t==u . Return convertible to bool |
addable<T> |
T operator+(T, const T&) |
t+=t1 . Return convertible to T |
addable<T,U> |
T operator+(T, const U&) |
t+=u . Return convertible to T |
subtractable<T> |
T operator-(T, const T&) |
t-=t1 . Return convertible to T |
subtractable<T,U> |
T operator-(T, const U&) |
t-=u . Return convertible to T |
multipliable<T> |
T operator*(T, const T&) |
t*=t1 . Return convertible to T |
multipliable<T,U> |
T operator*(T, const U&) |
t*=u . Return convertible to T |
dividable<T> |
T operator/(T, const T&) |
t/=t1 . Return convertible to T |
dividable<T,U> |
T operator/(T, const U&) |
t/=u . Return convertible to T |
modable<T> |
T operator%(T, const T&) |
t%=t1 . Return convertible to T |
modable<T,U> |
T operator%(T, const U&) |
t%=u . Return convertible to T |
orable<T> |
T operator|(T, const T&) |
t|=t1 . Return convertible to T |
orable<T,U> |
T operator|(T, const U&) |
t|=u . Return convertible to T |
andable<T> |
T operator&(T, const T&) |
t&=t1 . Return convertible to T |
andable<T,U> |
T operator&(T, const U&) |
t&=u . Return convertible to T |
xorable<T> |
T operator^(T, const T&) |
t^=t1 . Return convertible to T |
xorable<T,U> |
T operator^(T, const U&) |
t^=u . Return convertible to T |
incrementable<T> |
T operator++(T& x, int) |
T temp(x); ++x; return temp; Return convertible to T |
decrementable<T> |
T operator--(T& x, int) |
T temp(x); --x; return temp; Return convertible to T |
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.
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>;
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);
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:
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[]
).
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.
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 . |
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 boolT temp(x); ++x; return temp; (&*i.) . Return convertible to V* . |
bidirectional_iterator_helper <T,V,D,P,R> |
Same as above, plusT operator--(T& x, int) |
Same as above, plusT temp(x); --x; return temp; |
random_access_iterator_helper <T,V,D,P,R> |
Same as above, plusT operator+(T x, const D&) |
Same as above, plusx+=n . Return convertible to T x-=n . Return convertible to T x1<x2 . Return convertible to boolAnd to satisfy RandomAccessIterator: x1-x2 . Return convertible to D |
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.
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 03 Aug 2000
© 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.