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


Joe Buck <jbuck@racerx.synopsys.com> writes:

| > | > 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? 

No.

[...]

| 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.

At a first reading, it may seem that you get the same effect.  But in
fact, that is not the case -- there has been a long thread on that
topic some time ago somewhere on comp.std.c++ or comp.lang.moderated.
I won't repeat it here, suffices it to say that even though sometime
it may look like a partial specialization, it is not a partial
specialization. Somes are even proposing to add partial
specialization, which indicates that it isn't there.

| > 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 == .

Right.

| 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!

Right.  But they don't perform identically on X<int>.  Which is the whole
purpose of the exercise.  

The point being that, on saturday morning the algorithm chooses one
operator and on sunday night it chooses another.  My point is that it
should require the same constraints on all inputs to mirror standard
requirement.

| 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.

Given the contraints on the algorithm, the only safe assumption we can
make is that an operator== is present. Any assumption beyond that is
unsafe. Which is why retaining the algorithm as written with an
additional using-declaration as a fallback is unaccpetable.

| 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.

The situation is rather different and I won't attempt any comparaison
nor accept any conclusion drawn from any comparaison between them.

[...]

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

It is needless to that I dissmiss #1 and favor fix #2.

-- Gaby


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