0

With std::min(), we can std::min(a, b). But, what if I want min(a, b, c) or min(a, b, c, d, e)? I know the following implementation works:

template <typename T>
const T& min(const T& x, const T& y) {
  return x < y ? x : y;
}

template <typename T, typename... Ts>
const T& min(const T& x, const T& y, const Ts&... xs) {
  return min(min(x, y), xs...);
}

But I want a single succinct function (provided that it's possible at all, of course). I've tried the following

using std::swap;
template <typename T, typename... Ts>
const T& min(const T& x, const T& y, const Ts&... xs) {
  return min(min(x, y), xs...);
}

This does not work with min(1, 2, 3). This issue can be solved if I can just import a specific overload of std::swap(), which unfortunately doesn't seem possible in current C++. So, I'm asking, is there a succinct implementation that achieves what I want with just a single function? Note that I'm considering up to C++14. No C++1z please.

Lingxi
  • 14,579
  • 2
  • 37
  • 93
  • 7
    Why not use `std::min({a, b, c})`, `std::min({a, b, c, d, e})`, etc.? This works out of the box and is constexpr. – ildjarn Jan 25 '16 at 10:30
  • 1
    I don't see what is wrong with your first version. Why the restriction of a single function? Bear in mind that it is a template and will generate a function for each call (with different `Ts`), so this restriction seems pointless. – bolov Jan 25 '16 at 10:46
  • @ildjarn What if `a`, `b`, `c`, etc are expensive to copy, or are simply non-copyable? – Lingxi Jan 25 '16 at 11:37
  • @Lingxi : In that case, given `using std::cref;`, `std::min({cref(a), cref(b), cref(c)})`, etc. – ildjarn Jan 25 '16 at 11:57
  • @ildjarn I would say `min(a, b, c)` is much better than `min({cref(a), cref(b), cref(c)})`. Moreover, [this](http://coliru.stacked-crooked.com/a/b64645bd0e087f98) does not work. – Lingxi Jan 25 '16 at 12:06
  • 2
    @ildjarn The issue is related to [this](http://stackoverflow.com/q/30263007/1348273). – Lingxi Jan 25 '16 at 12:13
  • 2
    `min({cref(a), cref(b), cref(c)}, std::less>{})` or simply `std::less` since you know the types are the same, and the initializer_list would give an error if they aren't. – Jonathan Wakely Jan 25 '16 at 13:10

2 Answers2

3

I'll bite:

#include <initializer_list>
#include <algorithm>
#include <functional>

template<template<typename> class CompT = std::less, typename T, typename... Ts>
T const& variadic_min(T const& a, T const& b, Ts const&... ts) {
    return std::min({ std::cref(a), std::cref(b), std::cref(ts)... }, CompT<T>{});
}

Online Demo

One could also add an appropriate static_assert to improve the error message in the case that not all types are the same.

ildjarn
  • 62,044
  • 9
  • 127
  • 211
  • Ingenious! It also allows certain degree of customization on the predicate. There are a few minor issues, though. [This](https://github.com/Lingxi-Li/CPP_Utility/blob/master/cppu/algorithm.hpp#L70) should be the version to be shipped. – Lingxi Jan 25 '16 at 13:35
  • @Lingxi : Pedantically, that allows a single-argument invocation (which doesn't make sense), and what's being supplied is a comparator, not a predicate. ;-] – ildjarn Jan 25 '16 at 13:38
  • Pedantically, since `std::min()` allows it, so should we ;-] – Lingxi Jan 25 '16 at 13:44
  • @Lingxi : Fair enough! – ildjarn Jan 25 '16 at 13:44
  • Not to mention the mathematical notions on partially ordered sets, which allow for sets of one element. Solid answer though. – Luc Danton Jan 25 '16 at 13:47
  • If you are interested in writing trinkets like this, feel free to join the [party](https://github.com/Lingxi-Li/CPP_Utility) and play together :) – Lingxi Jan 25 '16 at 14:00
0

You could use std::min_element:

auto lst = {4, 3, 1, 2, 5};
auto iter = std::min_element(std::begin(lst), std::end(lst));
std::cout << *iter << '\n'; // 1
Brian Rodriguez
  • 4,250
  • 1
  • 16
  • 37