In this interview Stepanov shows how to implement generic max
function in C++.
Try to implement a simple thing in the object oriented way, say, max. I do not know how it can be done. Using generic programming I can write:
template <class StrictWeakOrdered> inline StrictWeakOrdered& max(StrictWeakOrdered& x, StrictWeakOrdered& y) { return x < y ? y : x; } and template <class StrictWeakOrdered> inline const StrictWeakOrdered& max(const StrictWeakOrdered& x, const StrictWeakOrdered& y) { return x < y ? y : x; }
(you do need both & and const &).
Why is there need to write the code twice? Is this needed to aid compiler for optimization or a convention to reduce bugs? Is max
a special case where body of a const
version is identical?
How many valid const
and non-const
permutations a function of N arguments should have to define a complete API?
First of all, you need the non-const
version to allow stuff like
max(a, b) = something;
If you don't want to do such things, you can just provide the const
version only to cover all cases. That is basically what the standard std::max
does.
You also do not need to provide any more permutations of const
and non-const
, returning non-const&
only makes sense if all inputs are non-const
, all other cases are properly handled by the const
version.
If you want to avoid code duplication, you can do something like this:
template <class StrictWeakOrdered>
inline StrictWeakOrdered& max(StrictWeakOrdered& x, StrictWeakOrdered& y) {
const auto &xr = x;
const auto &yr = y;
return const_cast<StrictWeakOrdered&>(max(xr, yr));
}
In this special case, the const_cast
is safe because you already know that the input is really non-const
. Now you only have to provide the implementation for the const
case.
So providing the implementation twice is not required and should not help the compiler, but whether or not the above is more readable than what Stepanov did is debatable.
You actually don't need both versions. You can write it this way.
template <class S, class T>
decltype(auto) max(S&& a, T&& b) {
using Ret = std::conditional_t<
std::is_const<std::remove_reference_t<S>>::value, S, T>;
if (b < a)
return std::forward<Ret>(a);
return std::forward<Ret>(b);
}
Falling back to const
if either of the arguments was const
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With