Why use “b < a ? a : b” instead of “a < b ? b : a” to implement max template?

Solution 1:

std::max(a, b) is indeed specified to return a when the two are equivalent.

That's considered a mistake by Stepanov and others because it breaks the useful property that given a and b, you can always sort them with {min(a, b), max(a, b)}; for that, you'd want max(a, b) to return b when the arguments are equivalent.

Solution 2:

This answer explains why the given code is wrong from a C++ standard point-of-view, but it is out of context.

See @T.C.'s answer for a contextual explanation.


The standard defines std::max(a, b) as follows [alg.min.max] (emphasis is mine):

template<class T> constexpr const T& max(const T& a, const T& b);

Requires: Type T is LessThanComparable (Table 18).

Returns: The larger value.

Remarks: Returns the first argument when the arguments are equivalent.

Equivalent here means that !(a < b) && !(b < a) is true [alg.sorting#7].

In particular, if a and b are equivalent, both a < b and b < a are false, so the value on the right of : will be returned in the conditional operator, so a has to be on the right, so:

a < b ? b : a

...seems to be the correct answer. This is the version used by libstdc++ and libc++.

So the information in your quote seems wrong according to the current standard, but the context in which it is defined might be different.

Solution 3:

The point is which one should be returned when they are equivalent; std::max has to return a (i.e. the first argument) for this case.

If they are equivalent, returns a.

So a < b ? b : a should be used; on the other hand, b < a ? a : b; will return b incorrectly.

(As @Holt said, the quote seems opposite.)

"the two values are equivalent but not equal" means they have the same value when being compared, but they migth be different objects at some other aspects.

e.g.

struct X { int a; int b; };
bool operator< (X lhs, X rhs) { return lhs.a < rhs.a; }
X x1 {0, 1};
X x2 {0, 2};
auto x3 = std::max(x1, x2); // it's guaranteed that an X which cantains {0, 1} is returned