2
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ 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? \$\endgroup\$ – lubgr Mar 21 at 7:48
  • 2
    \$\begingroup\$ There's a small bug in your code. min(a, b) should return a if a == b; it currently returns b. \$\endgroup\$ – papagaga Mar 21 at 10:38
  • \$\begingroup\$ @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 \$\endgroup\$ – Jose Fernando Lopez Fernandez Mar 22 at 11:26
3
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ 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 \$\endgroup\$ – Jose Fernando Lopez Fernandez Mar 22 at 11:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.