rel_ops (was Re: GCC 3.1 Release)

Joe Buck
Wed Apr 17 16:36:00 GMT 2002

Phil writes:
> In a perfect world (or at least in mine *grin*), we could tell the compiler
> that, in the case of ambiguous matches, check to see if one of the functions
> is in the std::rel_ops namespace, and if so, temporarily discard that one,
> and see if the ambiguity remains.  (Blue-sky idea, probably flaky.)

Such a trick, I believe, would conflict with the ISO standard, though
I suppose it could be considered an extension.
But no matter, it isn't needed.  I don't believe that it is difficult
to avoid conflicts with std::rel_ops.

To review this once more: std::rel_ops defines things like

template <class _Tp>
inline bool operator!=(const _Tp& __x, const _Tp& __y) {
  return !(__x == __y);

The things to note about this definition are:

1) it is completely general (seen by many as a bad thing, and in fact
   it would be a bad thing if it weren't confined to its own namespace;
   the user should choose to use it or not)

2) the types of both arguments are constrained to be the same.

Point #2 and the rules of C++ guarantee that any more-specific operator!=
definition will always be preferred to this one guarantee that there will
never be an ambiguity between this definition and any user definition of
operator!= that uses the same type for both arguments.

Conflicts will only occur if the user definition (or more specific
definition in the system library) permits the arguments to be of different
types but allows them to be of the same type.  If this happens, then it is
possible that, for a call to operator!= with two arguments that are of
exactly the same type, the two definitions of operator!= will be seen as
equal cost, and neither will be seen as a specialization of the other,
so the compiler will report an ambiguity.

The way to solve it is to add a third definition that will be seen by
the compiler as more specific than either of the alternatives.  Generally
speaking, this will be a symmetric form of the user definition.  In the
particular case that we just fixed, the problem was with the following

  template<typename _IteratorL, typename _IteratorR, typename _Container>
  inline bool
  operator!=(const __normal_iterator<_IteratorL, _Container>& __lhs,
	     const __normal_iterator<_IteratorR, _Container>& __rhs)
  { return __lhs.base() != __rhs.base(); }

Ambiguities will arise where we generate a match that uses the same
value for _IteratorL and _IteratorR.  For this case, both templates
generate exact matches, and neither covers a strict subset of the other's
range of applicability.

The trick of adding a third tie-breaker, in this case

  template<typename _Iterator, typename _Container>
  inline bool
  operator!=(const __normal_iterator<_Iterator, _Container>& __lhs,
             const __normal_iterator<_Iterator, _Container>& __rhs)
  { return __lhs.base() != __rhs.base(); }

applies in all such cases.  The above template always wins when both
it and the rel_ops template are exact matches.

More information about the Libstdc++ mailing list