The header <boost/operators.hpp>
supplies several sets of class templates (in namespace
boost
). These templates define operators at namespace
scope in terms of a minimal number of fundamental operators
provided by the class.
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 errorprone. The boost/operators.hpp
templates help by generating operators for you at namespace scope based
on other operators you've defined in your class.
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
(binary) +
. Twoargument forms of
the templates are also provided to allow interaction with other types.
addable
template requires operator+=(T const&)
and
in turn supplies operator+(T const&, T
const&)
.The discussed concepts are not necessarily the standard library's
concepts (CopyConstructible, etc.), although some of them could
be; they are what we call concepts with a small 'c'. In
particular, they are different from the former ones in that they do
not describe precise semantics of the operators they require to
be defined, except the requirements that (a) the semantics of the
operators grouped in one concept should be consistent (e.g.
effects of evaluating of a += b
and a = a + b
expressions should be the same), and (b) that the return types of the
operators should follow semantics of return types of corresponding
operators for builtin types (e.g. operator<
should return a type convertible to bool
, and
T::operator=
should return type convertible to
T
). Such "loose" requirements make operators
library applicable to broader set of target classes from different
domains, i.e. eventually more useful.
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 twoargument template forms of the
arithmetic operator templates are supplied for this purpose. When
applying the twoargument 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<T,
U>
, equality_comparable<T,
U>
, equivalent<T, U>
,
and partially_ordered<T, U>
)
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
twoargument forms must be specified by using the names shown below with
the trailing '2'
. The singleargument forms with the
trailing '1'
are provided for symmetry and to enable
certain applications of the base class chaining
technique.
Another application of the twoargument template forms is for
mixed arithmetics between a type T
and a type U
that is convertible to T
. In this case there are two ways
where the twoargument template forms are helpful: one is to provide
the respective signatures for operator overloading, the second is
performance.
With respect to the operator overloading assume e.g. that
U
is int
, that T
is an userdefined
unlimited integer type, and that double operator(double, const
T&)
exists. If one wants to compute int  T
and
does not provide T operator(int, const T&)
, the
compiler will consider double operator(double, const T&)
to be a better match than T operator(const T&, const
T&)
, which will probably be different from the user's intention.
To define a complete set of operator signatures, additional 'left' forms
of the twoargument template forms are provided (subtractable2_left<T, U>
,
dividable2_left<T, U>
,
modable2_left<T, U>
) that
define the signatures for noncommutative operators where U
appears on the left hand side (operator(const U&, const
T&)
, operator/(const U&, const T&)
,
operator%(const U&, const T&)
).
With respect to the performance observe that when one uses the
single type binary operator for mixed type arithmetics, the type
U
argument has to be converted to type T
. In
practice, however, there are often more efficient implementations of,
say T::operator=(const U&)
that avoid unnecessary
conversions from U
to T
. The twoargument
template forms of the arithmetic operator create additional operator
interfaces that use these more efficient implementations. There is, however,
no performance gain in the 'left' forms: they still need a conversion
from U
to T
and have an implementation
equivalent to the code that would be automatically created by the compiler
if it considered the single type binary operator to be the best match.
Every operator class template, except the arithmetic
examples and the iterator helpers, has an
additional, but optional, template type parameter B
. This
parameter will be a publiclyderived base class of the instantiated template.
This means it must be a class type. It can be used to avoid the bloating of
object sizes that is commonly associated with multipleinheritance from
several empty base classes (see the note for users of
older versions for more details). To provide support for a group of
operators, use the B
parameter to chain operator templates
into a singlebase class hierarchy, demostrated in the usage
example. The technique is also used by the composite operator templates
to group operator definitions. If a chain becomes too long for the compiler to
support, try replacing some of the operator templates with a single grouped
operator template that chains the old templates together; the length limit only
applies to the number of templates directly in the chain, not those hidden in
group templates.
Caveat: to chain to a base class which is
not a Boost operator template when using the singleargument 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.
On some compilers (e.g. Borland, GCC) even singleinheritance seems to cause an increase in object size in some cases. If you are not defining a class template, you may get better objectsize 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 struct less_than_comparable<myclass>; template struct equality_comparable<myclass>; template struct incrementable<myclass>; template struct decrementable<myclass>; template struct addable<myclass,long>; template struct subtractable<myclass,long>;
Note that some operator templates cannot use this workaround and must be a base class of their primary operand type. Those templates define operators which must be member functions, and the workaround needs the operators to be independent friend functions. The relevant templates are:
dereferenceable<>
indexable<>
Many compilers (e.g. MSVC 6.3, GCC 2.95.2) will not enforce
the requirements in the operator template tables unless the operations
which depend on them are actually used. This is not standardconforming
behavior. In particular, although 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
of those templates, this shortcut is not portable. Even if this currently
works with your compiler, it may not work later.
This example shows how some of the arithmetic operator templates can be used with a geometric point class (template).
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) autogenerated // by multipliable. point operator/=(T); // point operator/(point, const T&) autogenerated // 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 arithmetic operator templates ease the task of creating a custom numeric type. Given a core set of operators, the templates add related operators to the numeric class. These operations are like the ones the standard arithmetic types have, and may include comparisons, adding, incrementing, logical and bitwise manipulations, etc. Further, since most numeric types need more than one of these operators, some templates are provided to combine several of the basic operator templates in one declaration.
The requirements for the types used to instantiate the simple operator templates are specified in terms of expressions which must be valid and the expression's return type. The composite operator templates only list what other templates they use. The supplied operations and requirements of the composite operator templates can be inferred from the operations and requirements of the listed components.
These templates are "simple" since they provide operators
based on a single operation the base type has to provide. They have an
additional optional template parameter B
, which is not shown,
for the base class chaining technique.


Template  Supplied Operations  Requirements  

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 . See the Ordering Note. 

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 . t > u .Returns convertible to bool . See the Ordering Note. 

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 . 

subtractable2_left<T, U> 
T operator(const U&, const T&) 
T temp(u); temp = t .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 . 

dividable2_left<T, U> 
T operator/(const U&, const T&) 
T temp(u); temp /= t .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 . 

modable2_left<T, U> 
T operator%(const U&, const T&) 
T temp(u); temp %= t .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> 
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 . 

left_shiftable<T> left_shiftable1<T> 
T operator<<(T, const T&) 
t <<= t1 .Return convertible to T . 

left_shiftable<T, U> left_shiftable2<T, U> 
T operator<<(T, const U&) 
t <<= u .Return convertible to T . 

right_shiftable<T> right_shiftable1<T> 
T operator>>(T, const T&) 
t >>= t1 .Return convertible to T . 

right_shiftable<T, U> right_shiftable2<T, U> 
T operator>>(T, const U&) 
t >>= u .Return convertible to T . 

equivalent<T> equivalent1<T> 
bool operator==(const T&, const T&) 
t < t1 .Return convertible to bool . See the Ordering Note. 

equivalent<T, U> equivalent2<T, U> 
bool operator==(const T&, const U&) 
t < u . t > u .Returns convertible to bool . See the Ordering Note. 

partially_ordered<T> partially_ordered1<T> 
bool operator>(const T&, const T&) bool operator<=(const T&, const T&) bool operator>=(const T&, const T&) 
t < t1 . t == t1 .Returns convertible to bool . See the Ordering Note. 

partially_ordered<T, U> partially_ordered2<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 . t > u . t == u .Returns convertible to bool . See the Ordering Note. 
The less_than_comparable<T>
and partially_ordered<T>
templates provide the same set of operations. However, the workings of
less_than_comparable<T>
assume that all values of type T
can be placed in a total order. If
that is not true (e.g. NotaNumber values in IEEE floating point
arithmetic), then
partially_ordered<T>
should be used. The
partially_ordered<T>
template can be used for a totallyordered type, but it is not as efficient as
less_than_comparable<T>
.
This rule also applies for
less_than_comparable<T,
U>
and partially_ordered<T,
U>
with respect to the ordering of all T
and
U
values, and for both versions of
equivalent<>
. The solution for
equivalent<>
is to write a
custom operator==
for the target class.
The following templates provide common groups of related operations.
For example, since a type which is addable is usually also subractable, the
additive
template provides the combined
operators of both. The grouped operator templates have an additional
optional template parameter B
, which is not shown, for the
base class chaining technique.


Template  Component Operator Templates  

totally_ordered<T> totally_ordered1<T> 

totally_ordered<T, U> totally_ordered2<T, U> 

additive<T> additive1<T> 

additive<T, U> additive2<T, U> 

multiplicative<T> multiplicative1<T> 

multiplicative<T, U> multiplicative2<T, U> 

integer_multiplicative<T> integer_multiplicative1<T> 

integer_multiplicative<T, U> integer_multiplicative2<T, U> 

arithmetic<T> arithmetic1<T> 

arithmetic<T, U> arithmetic2<T, U> 

integer_arithmetic<T> integer_arithmetic1<T> 

integer_arithmetic<T, U> integer_arithmetic2<T, U> 

bitwise<T> bitwise1<T> 

bitwise<T, U> bitwise2<T, U> 

unit_steppable<T> 

shiftable<T> shiftable1<T> 

shiftable<T, U> shiftable2<T, U> 

ring_operators<T> ring_operators1<T> 

ring_operators<T, U> ring_operators2<T, U> 

ordered_ring_operators<T> ordered_ring_operators1<T> 

ordered_ring_operators<T, U> ordered_ring_operators2<T, U> 

field_operators<T> field_operators1<T> 

field_operators<T, U> field_operators2<T, U> 

ordered_field_operators<T> ordered_field_operators1<T> 

ordered_field_operators<T, U> ordered_field_operators2<T, U> 

euclidian_ring_operators<T> euclidian_ring_operators1<T> 

euclidian_ring_operators<T, U> euclidian_ring_operators2<T, U> 

ordered_euclidian_ring_operators<T> ordered_euclidian_ring_operators1<T> 

ordered_euclidian_ring_operators<T, U> ordered_euclidian_ring_operators2<T, U> 
The arithmetic operator class templates operators<>
and operators2<>
are examples of
nonextensible operator grouping classes. These legacy class templates,
from previous versions of the header, cannot be used for
base class chaining.


Template  Component Operator Templates  

operators<T> 

operators<T, U> operators2<T, U> 
The operators_test.cpp program demonstrates the use of the arithmetic operator templates, and can also be used to verify correct operation. Check the compiler status report for the test results with selected platforms.
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
noniterator 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 pointertomember operator>
and
the subscript operator[]
)!
The requirements for the types used to instantiate the dereference operators are specified in terms of expressions which must be valid and their return type. The composite operator templates list their component templates, which the instantiating type must support, and possibly other requirements.
All the dereference operator templates in this table accept an optional template parameter (not shown) to be used for base class chaining.


Template  Supplied Operations  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 five iterator operator class templates, each for a different
category of iterator. The following table shows the operator groups
for any category that a custom iterator could define. These class
templates have an additional optional template parameter B
,
which is not shown, to support base class chaining.


Template  Component Operator Templates  

input_iteratable<T, P> 

output_iteratable<T> 

forward_iteratable<T, P> 

bidirectional_iteratable<T, P> 

random_access_iteratable<T, P, D, R> 
There are also five iterator helper class templates, each corresponding
to a different iterator category. These classes cannot be used for base class chaining. The following summaries
show that these class templates supply both the iterator operators from
the iterator operator class templates and
the iterator typedef's required by the C++ standard (iterator_category
,
value_type
, etc.).


Template  Operations & Requirements  

input_iterator_helper<T, V, D, P, R> 
Supports the operations and has the requirements of  
output_iterator_helper<T> 
Supports the operations and has the requirements of See also [1], [2].  
forward_iterator_helper<T, V, D, P, R> 
Supports the operations and has the requirements of  
bidirectional_iterator_helper<T, V, D, P, R> 
Supports the operations and has the requirements of  
random_access_iterator_helper<T, V, D, P, R> 
Supports the operations and has the requirements of
To satisfy RandomAccessIterator,
x1  x2 with return convertible to D
is also required. 
[1] Unlike other iterator helpers templates,
output_iterator_helper
takes only one template parameter  the type of
its target class. Although to some it might seem like an unnecessary
restriction, the standard requires difference_type
and
value_type
of any output iterator to be
void
(24.3.1 [lib.iterator.traits]), and
output_iterator_helper
template respects this
requirement. Also, output iterators in the standard have void pointer
and
reference
types, so the output_iterator_helper
does the
same.
[2] As selfproxying is the easiest and most common way to
implement output iterators (see, for example, insert [24.4.2] and stream
iterators [24.5] in the standard library), output_iterator_helper
supports the idiom by defining operator*
and operator++
member functions which just return a
nonconst reference to the iterator itself. Support for
selfproxying allows us, in many cases, to reduce the task of writing an output
iterator to writing just two member functions  an appropriate
constructor and a copyassignment operator. For example, here is a possible
implementation of boost::function_output_iterator
adaptor:
template<class UnaryFunction> struct function_output_iterator : boost::output_iterator_helper< function_output_iterator<UnaryFunction> > { explicit function_output_iterator(UnaryFunction const& f = UnaryFunction()) : func(f) {} template<typename T> function_output_iterator& operator=(T const& value) { this>func(value); return *this; } private: UnaryFunction func; };
Note that support for selfproxying does not prevent you from using output_iterator_helper
to ease any other, different kind of output iterator's implementation. If output_iterator_helper
's target type provides its own definition of operator*
or/and operator++
, then these operators will get used and the ones supplied by output_iterator_helper
will never be instantiated.
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: explicit test_iter(T* i =0); test_iter(const self& x); self& operator=(const self& x); Reference operator*() const; self& operator++(); self& operator(); self& operator+=(Distance n); self& operator=(Distance n); bool operator==(const self& x) const; bool operator<(const self& x) const; friend Distance operator(const self& x, const self& y); };
Check the compiler status report for the test results with selected platforms.
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 backwardcompatible 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 1224 bytes on various compilers for the Win32 platform,
instead of the expected 8 bytes.
Strictly speaking, it was not the library's faultthe 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] paragraph 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 lightweight 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 backwardcompatible.
Revised: 30 Oct 2001
Copyright © David Abrahams and Beman Dawes 19992001. 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.