This is the mail archive of the libstdc++@gcc.gnu.org mailing list for the libstdc++ project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: rel_ops issues


> | > Consider what should happen with:
> | > 
> | > 	#include <stdio.h>
> | > 
> | > 	namespace Mine
> | > 	{
> | > 	   template<typename T>
> | > 	   bool operator==(const X<T>& a, const X<T>& b)
> | > 	   {
> | > 	      printf("%p == %p\n", &a, &b);
> | > 	      return true;
> | > 	   }
> | > 
> | > 	   template<typename  T>
> | > 	   bool operator!=(const X<T>& a, const X<T>& b)
> | >            {
> | >               printf("%p != %p\n", &a, &b);
> | > 	      return false;
> | >            }
> | > 
> | > 	   struct Y : X<int> { };
> | > 	}

I wrote:
> | ...  Please show me where you put the using
> | directive,
> 
> Inside the function as you were suggesting.

You mean in std::equal, which would then be applied to struct Y, right?
You then show in your example that the user could type a != and get
the message for the ==.  So is that the end of the story?  I don't think
so.  Please read on.

> | ... and which code malfunctions, remembering that because of the
> | way partial specialization works, bool operator!=(const X<T>& a, const
> | X<T>& b) will be chosen over the one in rel_ops (there is no ambiguity).
> 
> No. First, there is nothing called function partial specialization.  We
> only have function overloading.

OK, I'm not using standard terminology.  There is template function
specialization, but that is not being used here.  However, for
function template overloading, if two template functions can be called
and one is more specialized than the other, the more specialized function
is called.  This is the feature I was misusing the term "partial
specialization" to refer to, since the effect is similar.

While it's non-normative, see chapter 13.3.2 of Stroustrup's 3rd edition,
"Function Template Overloading" for a discussion (Gaby, I'm sure you know
this, but this is for others who are trying to follow).  Since the end effect
is just the same as what we get with partial specialization (prefer
more specialized templates, with "more specialized" the same as for
partial specialization), I use the same mental concept for both.

> Second, the example I provided will
> involve a conversion so the exact match provided by the general
> template will be chosen -- contrary to what you're saying.

Ah, but now I've got you! :-)  I'm not talking about blindly putting
a using directive everywhere, but in one specific location.

We are talking about fixing std::equal.  Consider two alternatives.
(I will omit __STL_REQUIRES below).  It is currently

template <class _InputIter1, class _InputIter2>
inline bool equal(_InputIter1 __first1, _InputIter1 __last1,
                  _InputIter2 __first2) {
  for ( ; __first1 != __last1; ++__first1, ++__first2)
    if (*__first1 != *__first2)
      return false;
  return true;
}

But this breaks if the type pointed to by the iterator does not have
operator !=, though it does have operator ==, and the standard says
only that it needs operator == .

Alternative #1 (my proposal): change it to

template <class _InputIter1, class _InputIter2>
inline bool equal(_InputIter1 __first1, _InputIter1 __last1,
                  _InputIter2 __first2) {
  using std::rel_ops::operator!=;
  for ( ; __first1 != __last1; ++__first1, ++__first2)
    if (*__first1 != *__first2)
      return false;
  return true;
}

But, you want to reject this, because in your example we will print
a message referring to == rather than !=.  OK, fine.  Let's switch
to doing it the other way.

Alternative #2:

template <class _InputIter1, class _InputIter2>
inline bool equal(_InputIter1 __first1, _InputIter1 __last1,
                  _InputIter2 __first2) {
  for ( ; __first1 != __last1; ++__first1, ++__first2)
    if (!(*__first1 == *__first2))
      return false;
  return true;
}

Guess what?  Alternatives #1 and #2 perform identically on your class Y!
Both will invoke your

 	   bool Mine::operator==(const X<T>& a, const X<T>& b);

So what can we conclude about the safety of
	using std::rel_ops::operator!=; ?

It is safe in those cases where it is acceptable to define != in terms of
==.  Since templates are in many respects macro-like, we are effectively
replacing some but not all != operators with ! and == in that scope.
If this is safe, the using directive is safe.  If it is not, the directive
is not.

The situation in some respects resembles the issue with optimizing away
copy constructors in the early days of C++.  We'd get into hot arguments
and people would post examples like yours: some messages would not print
out!  Since different compilers would optimize away different sets of copy
constructors, output would vary.  Finally the committee just settled it:
the compiler can assume that a copy constructor's side effects can be
ignored.

Effectively,
	using namespace std::rel_ops;

is the same kind of directive, though of course there are important
differences.  It can be thought of as a promise to the compiler that,
within its scope, the relational operators have a specific mathematical
relationship to each other.  If we apply STL algorithms to classes that
don't have this relationship, it's not going to work anyway.

However, for the sake of harmony I will accept either alternative above
as a fix for std::equal.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]