template <typename Less, typename T, typename... Ts>
constexpr const T& min(Less less, const T& a, const T& b, const Ts&... rems) {
This function requires a minimum of 2 elements. A minimum element will exist if the user provides a single argument variadic list. Consider handling that.
auto& min1 = min(std::less<>{}, 4, 5); // okay!
auto& min2 = min(std::less<>{}, 4); // candidate function not viable, reqs 3 args, 2 provided
return min(less, std::min(a, b, less), rems...);
Your approach here uses recursion and implementations are permitted to put a recursion depth limit on constexpr
calculations. Consider an iterative solution that expands the pack while calculating the minimum.
template <typename Comparator, typename First, typename... Rest>
constexpr decltype(auto) variadic_min(Comparator cmp, First const& first, Rest const&... rest) {
const First* result = std::addressof(first);
// cast each evaluated expr to void in case of overloaded comma operator shenanigans
((void)(result = std::addressof(std::min(*result, rest, cmp))), ...);
return *result;
}
An explanation with what is going on with the fold expression:
((void)(result = std::addressof(std::min(*result, rest, cmp))), ...);
^ ^ ^ ^
| | | expand using comma op
| | safer than built-in, now constexpr in 17
| evaluate the expression for each pack variable
cast to void the entire expression to avoid overloaded comma op.
Just thinking beyond the standard library and reinventing the wheel. This min
works fine for homogenous packs. What about heterogenous packs?
auto& min1 = min(std::less<>{}, 4, 5, -1); // min1 = -1
auto& min2 = min(std::less<>{}, 4, 5, -1.); // candidate template ignored...
One of the benefits of the conditional operator (?:
) is that if both resulting expressions return lvalues of the same type, then the result will have the same type. If that type is T&
, we could assign to that variable. Could we mimic that behavior with min
and friends?
auto a = 4;
auto b = 5;
((b < a) ? b : a) = 42; // a = 42, b = 5
min(std::less<>{}, a, b) = 42; // cannot assign to return value, returns const-qualified type
std::min(a, b) = 42; // same with standard library.
min(std::less<>{}, 5, 6) = 42 // cannot assign, makes sense!
Less
supposed to be a comparison object? (Like one that implement "less than" for typeT
?) \$\endgroup\$std::less<void>
would also fit :) \$\endgroup\$min(min(min(min(...
it should expand tomin(min(min(...), min(...)), min(min(), min()))
\$\endgroup\$