A well-designed interface would not prevent the use of a stateful comparison predicate.
And for free, you get a nicer implementation of the other alternatives.
template<typename CMP> float order(float a, float b, CMP cmp = {}) {
return cmp(a, b) ? a : b;
}
NOTE 1:
Despite popular belief, if CMP
is not default constructible, the syntax will still work; but of course, you will need to pass a way to construct it.
Which is good.
test:
#include <iostream>
#include <functional>
template<typename CMP>
float order(float a, float b, CMP cmp = {}) {
return cmp(a,b) ? a : b;
}
template<class T>
struct LESS : std::less<T> {
LESS() = delete;
LESS(const char*) {}
};
int main() {
const float A = order<std::less <float>>(3.1, 7.2);
const float B = order<std::greater<float>>(3.1, 7.2);
const float C = order< LESS <float>>(3.1, 7.2, "less");
// const float D = order< LESS <float>>(3.1, 7.2); // compile error, good!
std::cout << "A=" << A << "\n";
std::cout << "B=" << B << "\n";
return 0;
}
NOTE 2:
A better-designed interface will have a default order:
template<typename CMP = std::less<float> >
float order(float a, float b, CMP cmp = {}) {
return cmp(a,b) ? a : b;
}
NOTE 3:
Technically, if the class has an explicit default constructor, one needs something more verbose for this to work, but I wouldn't implement this way because, if the default constructor is explicit, there must be a good reason for it, and we are basically overriding the intended behavior of the class designer.
template<typename CMP = std::less<float> >
float order(float a, float b, CMP cmp = CMP{}) { // MMM, good idea?
return cmp(a,b) ? a : b;
}
Bonus:
Some people (including STL, https://en.cppreference.com/w/cpp/algorithm/sort) do not tolerate default parameters:
template<typename CMP>
float order(float a, float b, CMP cmp) {
return cmp(a,b) ? a : b;
}
template<typename CMP>
float order(float a, float b) {
return order<CMP>(a, b, {}); // or CMP{});
}
float order(float a, float b) {
return order<std::less<float>>(a, b);
}