4

I am reading the "Templates and Generic Programming" part in C++ Primer(5th Edition) but I got confused by some of the stuff there.

When talking about "Writing Type-Independent Code" at P655 & P656, the author stated that "The tests in the body use only < comparisons " , because "by writing the code using only the < operator, we reduce the requirements on types that can be used with our compare function. Those types must support <, but they need not also support >." .

Are there any types that support < but not > ? If so, why < has the superiority over > ? I have searched on Google for some time but I failed to get the answer. Could anybody give me some examples or some referral links?

hackjutsu
  • 8,336
  • 13
  • 47
  • 87
  • 2
    You're free to ignore this particular piece of advice. I do all the time. The only advantage is as stated; it's considerably easier, when writing throwaway code, to implement only `<` and not `>`. (Conversely, it can be more readable to use `<`, `<=`, `>`, and `>=` wherever they're appropriate.) – tmyklebu Jun 27 '14 at 00:44
  • I think the point of this is you only have to define the code for < comparison and the compiler can use this as a basis for other comparisons: for a < b, use (a < b) ; for a <= b use ~(b < a), and perhaps for a == b use ~((a < b) || (b < a)) . – rcgldr Jun 27 '14 at 03:12
  • @rcgldr Not the compiler, the programmer. –  Jun 27 '14 at 05:37
  • Note that `a < b` **is not** equivalent to `!(a >= b)` in the general case. Consider, for example, NaN, infinity, etc. in floating-point numbers. – user541686 Jun 27 '14 at 05:42
  • Only < is really needed. >, <= and >= are syntactic sugar. –  Jun 27 '14 at 06:59
  • IMHO, you shouldn't use `<` either in generic programming, but a (single) comparison function object. Per convention, this comparison function object defaults to what `<` does. – dyp Jun 27 '14 at 09:39
  • 2
    Probably, using `<` primarily has been introduced by Stepanov. He talks about the choice shortly in this video: http://youtu.be/B5yiLvaxPS4?t=34m21s – dyp Jun 27 '14 at 09:51

3 Answers3

3

If so, why < has the superiority over >?

Mere convention. The character '<' comes first in ASCII, and less-than over a partially-ordered set is a primitive from which the comparisons may be constructed (i.e., the set is partitioned into equivalence groups).

If we have a, b, and operator <:

  • a < b if operator <( a, b )
  • a > b if operator <( b, a )
  • a ≤ b if ! operator <( b, a )
  • a = b if ! operator <( a, b ) && ! operator <( b, a )
  • etc.

C++ has a number of such conventions which are used to describe how types behave. These are often called concepts, and an upcoming language extension, capital-C Concepts, will allow you to query and specify such things as whether the less-than operator defines a partial ordering.

Are there any types that support < but not >?

Yes, many. The convention is to define operator < and then forget about > because it would be redundant. You can use something like std::relops to automatically define > in terms of <, but overall it's easier just to avoid writing the > operator in the first place.

All the parts of the standard library that depend on ordering, such as std::sort and std::map, will never use >.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • Thank you for the thorough answers from your guys. Though there are many inspiring answers, Potatoswatter's is the most targeted one to my questions :) – hackjutsu Jun 27 '14 at 19:36
2

In some languages (like Haskell), there is this concept of minimal complete definition. For example, if you have defined a new class (e.g. for rational numbers) with a total order, it is sufficient to define the <= operator. The other operators can be (in some cases automatically) defined in terms of the minimal definition:

x > y can be defined as ! (x <= y)

x == y can be defined as x <= y && y <= x

x >= y can be defined as x > y || x == y

x < y can be defined as ! (x >= y)

In this case, the minimal definition has only one operator. In general, you only have to define the operators in a minimal complete definition, and doing so will reduce redundant definition of the other operators that can be "inferred". Implementing only the minimal definition also reduces the chances that you introduce errors or inconsistency while implementing the non-essential operators (like >) in your case. The point is that it is often beneficial to implement a minimal set of operators.

Regarding why < is preferred over >, I think this is rather a matter of choice and convention. Strictly speaking, one could have chosen to implement any minimal definition. In the case of total order types, each of {<=} and {>=} is minimal complete set. So those types must support one of these sets, but they need not also support the rest of them.

thor
  • 21,418
  • 31
  • 87
  • 173
  • `x >= y` is the same as `!(x < y)`. Your answer claims it is `!(x <= y)` which is incorrect. – Ben Voigt Jun 27 '14 at 01:39
  • Functional completeness is a mathematical concept, not higher languages's or Haskell's. Also `not` `<=` is actually `>` instead of `>=`. The right way to derive all other operators starting from `a <= b` would be to first derive `a == b` which can be defined as `a <= b` and `b <= a`, then derive `a >= b` which can be defined as `b <= a`, then derive `a < b` which can be defined as `not (b <= a)`, and finally `a > b` which can be defined as `not (a <= b)`. – Lie Ryan Jun 27 '14 at 01:48
  • Ben & Lie Thanks for the correction. I've fix the answer. – thor Jun 27 '14 at 05:35
  • 1
    I think it's easier to define `a >= b` as `b <= a`. Also, the canonical comparison in C++ is `<`, not `>`. It might be more illustrative to explain how all the comparisons may be constructed from `<`, since this isn't a Haskell question. – Potatoswatter Jun 27 '14 at 05:39
  • @Potatoswatter. I think `<=` is also a valid C++ operator. http://www.cplusplus.com/doc/tutorial/operators/. – thor Jun 27 '14 at 06:22
  • I'm not 100% sure about this. But I think `<=` implies that equality can be defined. `<` does not. So if `<` represent `is_a_subset_of`, we don't have `(!x x == y`. But with `<=` or `>=`, we can define equality as `(x <= y && y <= x ) <==> x == y`. – thor Jun 27 '14 at 06:28
  • @Ting: Transitivity of `<` implies equivalence classes. That's useful, because it allows e.g. `std::map` to be used with an ordering that isn't properly equality. Also, after your correction, `==` and `>=` are now recursively defined. Potatoswatter provides the fix for that. – Ben Voigt Jun 27 '14 at 06:37
  • @TingL Every total order is also partial order. The notion of "different things being equal" is somewhat moot; in programming terms it only means that there may be object state that is ignored by the set of comparison functions. – Potatoswatter Jun 27 '14 at 10:27
  • Ben, I've fixed the recursive definition issue following Lie's comment. I chose `<=` over `<` because `==` can be correctly defined. – thor Jun 27 '14 at 11:34
1

There are cases where a type might use operator < for no other reason than to use as a sorting key. It's not necessarily that the order is meaningful, it's just to support the very templated APIs that you're describing. For instance, std::map will keep all its elements sorted based on operator < unless you give it a different predicate. You may not care exactly what that order is, you just want to be able to have fast (O(log n)) lookups. The order may be completely meaningless to the class otherwise. (In that case, you'd probably want a free function binary version of operator < instead of making it a member, or provide an explicit predicate).

If other operations are meaningful to your class, then by all means implement them. It will make your users' lives much simpler. And you can always implement them in terms of operator < if it simplifies the logic.

The only reason I can think of that they used operator < instead of operator > is that it makes the order a bit more natural (assuming your language is written left-to-right). If you store the integers 1 through 5 in a map, they'll be ordered 1 < 2 < 3 < 4 < 5. So in the case of operator <(T left, T right) if the predicate returns true, the left operand will be more literally "to the left" of the right operand.

And after one more pass over the text you quoted, it sounds to me they're speaking more about the person writing the algorithm that uses operator < rather than the person writing the class that implements operator <. If you're writing an algorithm that needs to work with other people's code, and you want them to give you a way to order their elements, you should only require operator <. The point is to simplify work at the "client" side.

Cogwheel
  • 22,781
  • 4
  • 49
  • 67
  • "operator < more natural" - in the case of std::stable sort where the order is preserved for equal elements, to get a <= b, the template implements ~(b < a). This would be a case where it's not more "natural". – rcgldr Jun 27 '14 at 03:15
  • i'm not sure i understand your comment... in the case of a multimap, the ranges of equal value are arranged in the same order i'm describing. Within an equal value the ordering is coincidental, not determined by the operator – Cogwheel Jun 27 '14 at 15:38
  • For std::stable_sort where the order of "equal" elements is preserved (as opposed to std::sort), it would seem the "natural" compare operation for element a currently before element b in an array, vector, ... , would be (a <= b), but since the standard operator is <, std::stable_sort instead uses (b < a) and reverses the response, resulting in the equivalent of ~(b < a). How std::stable_sort preserves order using < instead of <= is a question that occasionally is asked. – rcgldr Jun 27 '14 at 21:21