# Replacing MIN and MAX macros with type-safe, recursive templates of variable arity

I'm currently reading Mastering Perl for Bioinformatics; I'm using the book for learning bioinformatics while implementing the code in C++. I came across a situation where I needed to use the minimum of three numbers, which the author did using a function call min3 which, as expected, returns the minimum of three numbers.

Rather than using the canonical C-style macro #define MIN(a,b) ..., I wanted to try and write more idiomatic C++, first by staying away from macros, and second by using templates to write type-safe, variadic Min and Max functions.

namespace Math
{
template <typename T1, typename T2>
constexpr inline auto Max(T1 a, T2 b) noexcept
{
return (a > b) ? a : b;
}

template <typename T1, typename T2, typename... Types>
constexpr inline auto Max(T1 a, T2 b, Types... args) noexcept
{
return Max(a, Max(b, args...));
}

template <typename T1, typename T2>
constexpr inline auto Min(T1 a, T2 b) noexcept
{
return (a < b) ? a : b;
}

template <typename T1, typename T2, typename... Types>
constexpr inline auto Min(T1 a, T2 b, Types... args) noexcept
{
return Min(a, Min(b, args...));
}
} // namespace Math


Example usage:

std::cout << Math::Max(1,2)       << std::cout.widen('\n');
std::cout << Math::Min(3,8,4,3,2) << std::cout.widen('\n');


Output:

2
2


I'm using std::cout.widen('\n') so as to not call std::fflush() on each call to std::endl.

• This happens to be extremely close to this question. Which assumes C++17 though, maybe you could clarify whether C++17 is available to you? – lubgr Mar 21 at 7:48
• There's a small bug in your code. min(a, b) should return a if a == b; it currently returns b. – papagaga Mar 21 at 10:38
• @lubgr I completely missed that question when I was verifying this wasn't a repost. I can't use C++17 in production yet though, so I suppose I got lucky – Jose Fernando Lopez Fernandez Mar 22 at 11:26

## 1 Answer

Your implementation will work nicely for integers, however, it might be doing a lot of copies which could hurt you for more expensive types.

An edge case that might sometimes be useful in case of calling this via a template: the min/max of 1 number.

Your noexcept is wrong in case of throwing copy constructors. You could change this to noexcept(std::is_nothrow_copy_constructable<T>) or fix the remark above and prevent copies.

Looking at the template arguments, you do allow T1 and T2 to be of a different type. I don't see much added value in that, as you would get an obscure error about the ?: operator.

And to end with a positive note: I really like the constexpr. This allows you to write a unit test as a static_assert.

• I thought allowing for different types might be handy in case you had to compare a long and a double, or something, but I do agree it doesn't seem great in retrospect. I really appreciate the feedback, this gave me a lot to think about – Jose Fernando Lopez Fernandez Mar 22 at 11:30