It works well, according to my simple test program:
#include <functional>
int main()
{
return min(std::less<int>(), 2, 0, 3);
}
I suggest that when you present code for review, you include the tests, too - I imagine that your testing is much more thorough than mine, and I'm too lazy to re-create the full suite. It also helps reviewers identify scenarios that are missing from the tests (or, conversely, scenarios that are redundant).
I recommend using perfect forwarding for the comparator:
constexpr const T& min(Less&& less,
// ^^
return std::min(a, b, std::forward<Less>(less));
// ^^^^^^^^^^^^^^^^^^
There's a couple of other uses that need to be forwarded, too.
As lubgr mentioned, it's worth using an initializer list. If we want to avoid copying (if the inputs are large, or can't be copied), then we'll want to use a initializer-list of reference wrappers, and use std::min_element() (since the initializer-list std::min() returns a copy). That can be achieved like this:
#include <algorithm>
#include <functional>
template<typename Less, typename... T>
constexpr auto& min(Less&& less, const T& ...values)
{
auto const compare_wrapped =
[&less](auto const&a, auto const& b) {
return std::forward<Less>(less)(a.get(), b.get());
};
auto const list = { std::ref(values)..., };
auto const smallest =
std::min_element(list.begin(), list.end(), compare_wrapped);
return smallest->get();
}
int main()
{
auto const a = 3;
auto const b = 0;
auto const c = 2;
auto const& lowest = min(std::less<int>(), a, b, c);
return &lowest != &b;
}
Or, more succinctly:
template<typename Less, typename... T>
constexpr auto& min(Less&& less, const T& ...values)
{
return std::min({std::cref(values)...,}, std::forward<Less>(less)).get();
}
One defect in this implementation is that it will accept xvalues and happily return a (useless) reference in that case. I think it ought to be possible to distinguish that case, and forward the chosen xvalue as result, but I haven't had time to implement that.