0

This is an extension of the standard std::max function, so it can take a random amount of arguments.

template<typename T, typename U, typename Compare = std::greater<>> constexpr
const auto max(const T& first, const U& second, Compare comp = Compare())
{
    return comp(first, second) ? first : second;
}

template<typename T, typename U, typename... Pack, typename Compare = std::greater<>> constexpr
const auto max(const T& first, const U& second, const Pack&... rest, Compare comp = Compare())
{
    return comp(first, second) ? max(first, rest..., comp) : max(second, rest..., comp);
}

As I understand, the comp parameter will take the value of the last argument with which I call the function.

Am I wrong? What can I do about it?

std::cout << max(2, 3, 4, 5); //boom
std::cout << max(2, 3, 4, 5, std::greater<>()); //boom

Of course it works fine if I remove comp entirely.

DeiDei
  • 10,205
  • 6
  • 55
  • 80
  • 3
    `rest` is in a non-deduced context. (This is pretty much why the "variadic" version of `std::max` takes a `initializer_list`.) – T.C. Dec 28 '15 at 13:20
  • 6
    Thus is impractical. Try something else. (I can make your code work, but it involves sfinae gymnastics and metaprogramming, and even then if you have a parameter that supports `()` and `<` it breaks). Make the compare version have a different name and put the compare first. – Yakk - Adam Nevraumont Dec 28 '15 at 13:22

1 Answers1

2

The type of rest is a non-deduced context, so the compilation fails as no matching function for the call to max is found (for background information, see What is a non-deduced context?):

example.cpp:18:18: error: no matching function for call to 'max'
    std::cout << max(2, 3, 4, 5); //boom
                 ^~~
example.cpp:5:12: note: candidate function template not viable: requires at most 3 arguments, but 4
      were provided
const auto max(const T& first, const U& second, Compare comp = Compare())
           ^
example.cpp:11:12: note: candidate function not viable: requires at most 3 arguments, but 4 were
      provided
const auto max(const T& first, const U& second, const Pack&... rest, Compare comp = Compare())

There is no easy solution to avoid that. You might consider one of the following workarounds:

  1. Pass the Compare parameter as the first argument
  2. Use initializer_list to pass the argument (thus, all will have the same type)

In the C++14 standard, they chose the second option:

template< class T, class Compare >
T max( std::initializer_list<T> ilist, Compare comp )
{
    return *std::max_element(ilist.begin(), ilist.end(), comp);
}

Source: http://en.cppreference.com/w/cpp/algorithm/max

Community
  • 1
  • 1
Philipp Claßen
  • 41,306
  • 31
  • 146
  • 239