From 4755b42909b10fd95fd9394cc434c489bb84d3e2 Mon Sep 17 00:00:00 2001
From: Dave Abrahams The header <boost/operators.hpp> supplies
+ several sets of class templates (in namespace The header <boost/operators.hpp>
-supplies several sets of class templates (in namespace
- Overloaded operators for class types typically occur in groups. If
-you can write If, for example, you declare a class like this: Overloaded operators for class types typically occur in groups. If you
+ can write If, for example, you declare a class like this:
-Header <boost/operators.hpp>
+ Header <boost/operators.hpp>
boost
). These
+ templates define operators at namespace scope in terms of a minimal
+ number of fundamental operators provided by the class.boost
). These templates define operators at namespace
-scope in terms of a minimal number of fundamental operators
-provided by the class.Contents
-Contents
+
+
+
+
-
+ Rationale
+
+
+ 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.
-
class MyInt
+
+
+
+ Rationale
+
+ 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.
+
+class MyInt
: boost::operators<MyInt>
{
bool operator<(const MyInt& x) const;
@@ -101,170 +144,175 @@ on other operators you've defined in your class.
then the operators<>
-template adds more than a dozen additional operators, such as
-operator>
, <=
, >=
, and
-(binary) +
. Two-argument forms of
-the templates are also provided to allow interaction with other types.
then the operators<>
+ template adds more than a dozen additional operators, such as
+ operator>
, <=
, >=
, and
+ (binary) +
. Two-argument 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&)
.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 built-in 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 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 built-in 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 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<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
.
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<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
-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.
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.
Another application of the two-argument 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 two-argument template forms are helpful: one is to provide
-the respective signatures for operator overloading, the second is
-performance.
Another application of the two-argument 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 two-argument 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 user-defined
-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 two-argument template forms are provided (subtractable2_left<T, U>
,
-dividable2_left<T, U>
,
-modable2_left<T, U>
) that
-define the signatures for non-commutative 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 operator overloading assume e.g. that
+ U
is int
, that T
is an
+ user-defined 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 two-argument template forms are provided
+ (subtractable2_left<T,
+ U>
, dividable2_left<T,
+ U>
, modable2_left<T,
+ U>
) that define the signatures for non-commutative
+ 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 two-argument
-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.
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 two-argument
+ 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 publicly-derived 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 multiple-inheritance 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 single-base 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.
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 publicly-derived 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 multiple-inheritance 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 single-base
+ 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 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.
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.
On some compilers (e.g. Borland, GCC) even single-inheritance seems -to cause an increase in object size in some cases. If you are not defining -a class template, you may get better object-size performance by avoiding -derivation altogether, and instead explicitly instantiating the operator -template as follows:
+On some compilers (e.g. Borland, GCC) even single-inheritance + seems to cause an increase in object size in some cases. If you are not + defining a class 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... { //... @@ -277,38 +325,43 @@ template as follows: 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:
+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<>
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 standard-conforming
-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).
+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 standard-conforming
+ 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! @@ -358,880 +411,1544 @@ 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 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.
+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.
+
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. |
-
+
Example Templates- -The arithmetic operator class templates
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. Check the compiler status report for the test results -with selected platforms. - -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 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. - -Dereference Operators- -All the dereference operator templates in this table accept an -optional template parameter (not shown) to be used for base class chaining. - -
Grouped Iterator Operators- -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
Iterator Helpers- -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 (
|
- 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. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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> |
+
+ bool operator==(const U&, const T&) + bool operator!=(const U&, const T&) + bool operator!=(const T&, const U&) |
+
+ t == u .+ Return convertible to bool . |
+ ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
addable<T> + addable1<T> |
+
+ T operator+(const T&, const T&) |
+
+ T temp(t); temp += t1 .+ Return convertible to T . See the Symmetry Note. |
+ ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
addable<T, U> + addable2<T, U> |
+
+ T operator+(const T&, const U&) + T operator+(const U&, const T& ) |
+
+ T temp(t); temp += u .+ Return convertible to T . See the Symmetry Note. |
+ ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
subtractable<T> + subtractable1<T> |
+
+ T operator-(const T&, const T&) |
+
+ T temp(t); temp -= t1 .+ Return convertible to T . See the Symmetry Note. |
+ ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
subtractable<T,
+ U> + subtractable2<T, U> |
+
+ T operator-(const T&, const U&) |
+
+ T temp(t); temp -= u .+ Return convertible to T . See the Symmetry Note. |
+ ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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*(const T&, const T&) |
+
+ T temp(t); temp *= t1 .+ Return convertible to T . See the Symmetry Note. |
+ ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
multipliable<T,
+ U> + multipliable2<T, U> |
+
+ T operator*(const T&, const U&) + T operator*(const U&, const T&) |
+
+ T temp(t); temp *= u .+ Return convertible to T . See the Symmetry Note. |
+ ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
dividable<T> + dividable1<T> |
+
+ T operator/(const T&, const T&) |
+
+ T temp(t); temp /= t1 .+ Return convertible to T . See the Symmetry Note. |
+ ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
dividable<T, U> + dividable2<T, U> |
+
+ T operator/(const T&, const U&) |
+
+ T temp(t); temp /= u .+ Return convertible to T . See the Symmetry Note. |
+ ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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%(const T&, const T&) |
+
+ T temp(t); temp %= t1 .+ Return convertible to T . See the Symmetry Note. |
+ ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
modable<T, U> + modable2<T, U> |
+
+ T operator%(const T&, const U&) |
+
+ T temp(t); temp %= u .+ Return convertible to T . See the Symmetry Note. |
+ ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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|(const T&, const T&) |
+
+ T temp(t); temp |= t1 .+ Return convertible to T . See the Symmetry Note. |
+ ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
orable<T, U> + orable2<T, U> |
+
+ T operator|(const T&, const U&) + T operator|(const U&, const T&) |
+
+ T temp(t); temp |= u .+ Return convertible to T . See the Symmetry Note. |
+ ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
andable<T> + andable1<T> |
+
+ T operator&(const T&, const T&) |
+
+ T temp(t); temp &= t1 .+ Return convertible to T . See the Symmetry Note. |
+ ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
andable<T, U> + andable2<T, U> |
+
+ T operator&(const T&, const U&) + T operator&(const U&, const T&) |
+
+ T temp(t); temp &= u .+ Return convertible to T . See the Symmetry Note. |
+ ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
xorable<T> + xorable1<T> |
+
+ T operator^(const T&, const T&) |
+
+ T temp(t); temp ^= t1 .+ Return convertible to T . See the Symmetry Note. |
+ ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
xorable<T, U> + xorable2<T, U> |
+
+ T operator^(const T&, const U&) + T operator^(const U&, const T&) |
+
+ T temp(t); temp ^= u .+ Return convertible to T . See the Symmetry Note. |
+ ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
incrementable<T> |
+
+ T operator++(T&, int) |
+
+ T temp(t); ++t + Return convertible to T . |
+ ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
decrementable<T> |
+
+ T operator--(T&, int) |
+
+ T temp(t); --t; + Return convertible to T . |
+ ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
left_shiftable<T> + left_shiftable1<T> |
+
+ T operator<<(const T&, const T&) |
+
+ T temp(t); temp <<= t1 .+ Return convertible to T . See the Symmetry Note. |
+ ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
left_shiftable<T,
+ U> + left_shiftable2<T, U> |
+
+ T operator<<(const T&, const U&) |
+
+ T temp(t); temp <<= u .+ Return convertible to T . See the Symmetry Note. |
+ ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
right_shiftable<T> + right_shiftable1<T> |
+
+ T operator>>(const T&, const T&) |
+
+ T temp(t); temp >>= t1 .+ Return convertible to T . See the Symmetry Note. |
+ ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
right_shiftable<T,
+ U> + right_shiftable2<T, U> |
+
+ T operator>>(const T&, const U&) |
+
+ T temp(t); temp >>= u .+ Return convertible to T . See the Symmetry Note. |
+ ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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. Not-a-Number values in IEEE floating point
+ arithmetic), then partially_ordered<T>
should be
+ used. The partially_ordered<T>
template can
+ be used for a totally-ordered 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.
Before talking about symmetry, we need to talk about optimizations to
+ understand the reasons for the different implementation styles of
+ operators. Let's have a look at operator+
for a class
+ T
as an example:
+T operator+( const T& lhs, const T& rhs ) +{ + return T( lhs ) += rhs; +} ++ This would be a normal implementation of
operator+
, but it
+ is not an efficient one. An unnamed local copy of lhs
is
+ created, operator+=
is called on it and it is copied to the
+ function return value (which is another unnamed object of type
+ T
). The standard doesn't generally allow the intermediate
+ object to be optimized away:
+
+ + 3.7.2/2: Automatic storage duration+ The reference to 12.8 is important for us: + +
+
+ If a named automatic object has initialization or a destructor with + side effects, it shall not be destroyed before the end of its block, + nor shall it be eliminated as an optimization even if it appears to be + unused, except that a class object or its copy may be eliminated as + specified in 12.8. +
+ 12.8/15: Copying class objects+ This optimization is known as the named return value optimization (NRVO), + which leads us to the following implementation for +
+ ...
+ For a function with a class return type, if the expression in the + return statement is the name of a local object, and the cv-unqualified + type of the local object is the same as the function return type, an + implementation is permitted to omit creating the temporary object to + hold the function return value, even if the class copy constructor or + destructor has side effects. +
operator+
:
++T operator+( const T& lhs, const T& rhs ) +{ + T nrv( lhs ); + nrv += rhs; + return nrv; +} ++ Given this implementation, the compiler is allowed to remove the + intermediate object. Sadly, not all compiler implement the NRVO, some + even implement it in an incorrect way which makes it useless here. + Without the NRVO, the NRVO-friendly code is no worse than the original + code showed above, but there is another possible implementation, which + has some very special properties: +
+T operator+( T lhs, const T& rhs ) +{ + return lhs += rhs; +} ++ The difference to the first implementation is that
lhs
is
+ not taken as a constant reference used to create a copy; instead,
+ lhs
is a by-value parameter, thus it is already the copy
+ needed. This allows another optimization (12.2/2) for some cases.
+ Consider a + b + c
where the result of
+ a + b
is not copied when used as lhs
+ when adding c
. This is more efficient than the original
+ code, but not as efficient as a compiler using the NRVO. For most people,
+ it is still preferable for compilers that don't implement the NRVO, but
+ the operator+
now has a different function signature. Also,
+ the number of objects created differs for
+ (a + b ) + c
and
+ a + ( b + c )
. Most probably,
+ this won't be a problem for you, but if your code relies on the function
+ signature or a strict symmetric behaviour, you should set
+ BOOST_FORCE_SYMMETRIC_OPERATORS
in your user-config. This
+ will force the NRVO-friendly implementation to be used even for compilers
+ that don't implement the NRVO. 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
+ non-extensible 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
+ 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[]
)!
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 self-proxying 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 non-const reference to the iterator itself. Support for
+ self-proxying 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 copy-assignment operator. For example, here is a
+ possible implementation of boost::function_output_iterator
+ adaptor:
template<class UnaryFunction> struct function_output_iterator @@ -1252,20 +1969,27 @@ struct function_output_iterator };-
Note that support for self-proxying 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.
Note that support for self-proxying 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.
+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> ++++}; ++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> @@ -1286,102 +2010,113 @@ public: 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.
+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 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] 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 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: 30 Oct 2001
+Copyright © David Abrahams and Beman Dawes 1999-2001. -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.
+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] 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 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: 30 Oct 2001
+ +Copyright © David Abrahams and Beman Dawes 1999-2001. 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.
+ +