Max function with variable number of arguments I find it irritating that in standard C++ I can't do std::max(a, b) = x when it's possible and that it can't handle more than 2 arguments. For the second concern I found this std::min tutorial and it looks interesting but the usage of pointers confuses me because I don't understand why they are needed here. Since I want to learn more about c++ I wanted to try to create my own version of std::max while making use of the latest c++ features that I know of.

First I want to make use of concepts to let the user know when the types of arguments are invalid at compilation time:

namespace std1 {

// Helper concept for static_assert
template&lt;typename&gt;
concept False = false;

template&lt;typename T&gt;
concept Boolean = std::is_same_v&lt;T, bool&gt;;

template&lt;typename T&gt;
concept TypeLessThanComparable = requires(T a, T b) {
    { a &lt; b } -&gt; Boolean;
};

template&lt;typename T&gt;
concept TypeLessThanEqComparable = requires(T a, T b) {
    { a &lt;= b } -&gt; Boolean;
};

template&lt;typename T&gt;
concept TypeGreaterThanComparable = requires(T a, T b) {
    { a &gt; b } -&gt; Boolean;
};

template&lt;typename T&gt;
concept TypeGreaterThanEqComparable = requires(T a, T b) {
    { a &gt;= b } -&gt; Boolean;
};


These are the functions themselves:

template&lt;typename T&gt;
constexpr decltype(auto) max(T&amp;&amp; a) noexcept
{
    return std::forward&lt;T&gt;(a);
}

template&lt;typename T&gt;
constexpr decltype(auto) max(T&amp;&amp; a, T&amp;&amp; b) noexcept
{
    if constexpr(TypeLessThanComparable&lt;T&gt;) {
        return a &lt; b ? std::forward&lt;T&gt;(b) : std::forward&lt;T&gt;(a);
    }
    else if constexpr(TypeLessThanEqComparable&lt;T&gt;) {
        return a &lt;= b ? std::forward&lt;T&gt;(b) : std::forward&lt;T&gt;(a);
    }
    else if constexpr(TypeGreaterThanComparable&lt;T&gt;){
        return a &gt; b ? std::forward&lt;T&gt;(a) : std::forward&lt;T&gt;(b);
    }
    else if constexpr(TypeGreaterThanEqComparable&lt;T&gt;) {
        return a &gt;= b ? std::forward&lt;T&gt;(a) : std::forward&lt;T&gt;(b);
    }
    else {
        // if I just put false in static_assert it gives a compilation error no matter what
        static_assert(False&lt;void&gt;, "You called max with invalid arguments, cannot find comparison operators for their type");
    }
}

template&lt;typename T, typename...Ts&gt;
constexpr decltype(auto) max(T&amp;&amp; a, T&amp;&amp; b, T&amp;&amp; c, Ts&amp;&amp;...d) noexcept
{
    return max(a, max(b, max(c, d...)));
}

} // namespace std1


And some tests:


struct A{};

struct B
{
    bool operator&lt;(B const&amp;) const noexcept = delete;
    bool operator&lt;=(B const&amp;) const noexcept = delete;
    bool operator&gt;(B const&amp;) const noexcept = delete;
    bool operator&gt;=(B const&amp;) const noexcept;
};

int main()
{
    static_assert(std1::max(1, 2) == 2);

    int a = 1;
    int b = 5;
    int c = 3;
    int d = 2;

    assert(std1::max(a, b, c, d) == b);

    std1::max(b, c, d) = 4;

    assert(b == 4);

    // This gives a compilation error because the static assertion failed
    // (void)std1::max(A{}, A{});

    // This works
    std1::max(B{}, B{}, B{}, B{}, B{});
}


I want to know if the code is well written and could replace std::max in c++20, maybe it has bugs that I'm not aware of since I am inexperienced in c++. You could also check the compiler explorer link: https://godbolt.org/z/5urgqR.
I think this function is unnecessary.

We can deal with defective classes (that don't properly implement the standard LessThanComparable concept) by either fixing them (preferable) or by providing a comparator argument to std::max:

auto const b_lessthan = [](const B&amp; a, const B&amp; b){ return !(a&gt;=b); };

std::max({B{}, B{}, B{}, B{}, B{}}, b_lessthan);


Sure, you could make a generic adapter using the same if constexpr chain as in this code, but are the defective types really that common?



We can arrange for std::max() to return an lvalue by passing it an initialiser list of std::reference_wrapper for its arguments:

template&lt;typename... T&gt;
constexpr auto&amp; ref_max(T... args)
{
    return std::max({std::ref(args)...,}).get();
}




We now have

#include &lt;algorithm&gt;
#include &lt;functional&gt;
#include &lt;cassert&gt;

int main()
{
    static_assert(std::max(1, 2) == 2);

    int a = 1;
    int b = 5;
    int c = 3;
    int d = 2;

    assert(std::max({a, b, c, d}) == b);

    ref_max(b, c, d) = 4;

    assert(b == 4);

    // This gives a compilation error because the static assertion failed
    // (void)std1::max(A{}, A{});

    // This works
    auto const b_lessthan = [](const B&amp; a, const B&amp; b){ return !(a&gt;=b); };

    std::max({B{}, B{}, B{}, B{}, B{}}, b_lessthan);
}


Which isn't so very different than the main() in the question.