Min function accepting varying number of arguments in C++17 Come across this problem once again in the book The Modern C++ Challenge.

18. Minimum function with any number of arguments
Write a function template that can take any number of arguments and returns the minimum value of them all, using operator &lt; for comparison. Write a variant of this function template that can be parameterized with a binary comparison function to use instead of operator &lt;.

I wonder how simple and elegant the implementation could be using c++17. The following is the parameterized version. Ideas?
#include &lt;algorithm&gt;

template &lt;typename Less, typename T, typename... Ts&gt;
constexpr const T&amp; min(Less less, const T&amp; a, const T&amp; b, const Ts&amp;... rems) {
  if constexpr (sizeof...(rems)) {
    return min(less, std::min(a, b, less), rems...);
  }
  else {
    return std::min(a, b, less);
  }
}
template &lt;typename Less, typename T, typename... Ts&gt;
constexpr const T&amp; min(Less less, const T&amp; a, const T&amp; b, const Ts&amp;... 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&amp; min1 = min(std::less&lt;&gt;{}, 4, 5);  // okay!
auto&amp; min2 = min(std::less&lt;&gt;{}, 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 &lt;typename Comparator, typename First, typename... Rest&gt;
constexpr decltype(auto) variadic_min(Comparator cmp, First const&amp; first, Rest const&amp;... 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&amp; min1 = min(std::less&lt;&gt;{}, 4, 5, -1);  // min1 = -1
auto&amp; min2 = min(std::less&lt;&gt;{}, 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&amp;, we could assign to that variable. Could we mimic that behavior with min and friends?

auto a = 4;
auto b = 5;
((b &lt; a) ? b : a)  = 42;        // a = 42, b = 5
min(std::less&lt;&gt;{}, a, b) = 42;  // cannot assign to return value, returns const-qualified type
std::min(a, b) = 42;            // same with standard library.
min(std::less&lt;&gt;{}, 5, 6) = 42   // cannot assign, makes sense!