Как избежать недопустимой перегрузки из стандартной библиотеки?

В моем проекте С++ 14 я использую

#include <algorithm>
using std::max;            // for max(a,b)

но я также хочу предоставить функцию max(), принимающую любое количество аргументов (равного типа). С этой целью я добавил

template<typename T, typename...A>
constexpr inline T const& max(T const&x, T const&y, A...&&z)
{
    return max(max(x,y), std::forward<A>(z)...);
}

но при вызове типа

double x,y,z;
...
auto m = max(x,y,z);

компилятор пытается вместо моей перегрузки использовать

namespace std {
template< class T, class Compare >
constexpr const T& max( const T& a, const T& b, Compare comp );
}

от algorithm. Как я могу избежать этого (но при этом обеспечить предполагаемую функциональность)?

(Обратите внимание, что есть вопрос о вариативном min/max, но он не решить мою проблему, просто изменив имя на vmin/vmax.)


person Walter    schedule 15.09.2020    source источник
comment
Существует перегрузка std::max, которая принимает список инициализаторов. Возможно, это сработало бы для вас вместо этого? en.cppreference.com/w/cpp/algorithm/max   -  person Retired Ninja    schedule 15.09.2020
comment
Не делайте using std::max -- везде используйте явное разрешение пространства имен.   -  person wcochran    schedule 15.09.2020
comment
@RetiredNinja это не решение, так как оно не обеспечивает предполагаемую функциональность. То же самое относится и к комментарию Вокрана.   -  person Walter    schedule 15.09.2020
comment
Следует отметить, что причина, по которой вызывается перегрузка std::max, заключается в том, что double может быть неявно преобразован в bool, что делает перегрузку более приоритетной, чем распаковка пакета параметров и продолжение расширения.   -  person mkamerath    schedule 15.09.2020
comment
@mkamerath почему бред?   -  person Oktalist    schedule 15.09.2020
comment
Почему бы не поместить max в отдельное пространство имен?   -  person Den-Jason    schedule 16.09.2020
comment
@ Ден-Джейсон, как это поможет?   -  person Walter    schedule 16.09.2020
comment
@Walter, неважно, я проверил вариант ответа, и он работает. Я предоставил демонстрацию для всех, кто может быть заинтересован.   -  person Den-Jason    schedule 16.09.2020


Ответы (4)


Проблема может быть решена только перегрузкой функции для 3 или более аргументов, например:

template<typename T, typename...A>
constexpr inline T const& max(T const&x, T const&y, T const&z, A&&...w)
{
    return max(max(x,y), z, std::forward<A>(w)...);
}
person yao99    schedule 15.09.2020
comment
Чтобы уточнить: работает ли это, потому что специализации этого шаблона (с A = []) считаются более конкретными, чем для шаблона в стандартной библиотеке, и поэтому, когда это возможно, выбирают этот шаблон? - person HTNW; 15.09.2020
comment
@HTNW Я так думаю. И я все еще читаю стандартный [over.match.best] . - person yao99; 15.09.2020

Вы не знаете.

Либо остановите using std::max и вместо этого определите свои пространства имен, либо назовите свою функцию как-то иначе.

person Asteroids With Wings    schedule 15.09.2020

Как указано в комментариях, вы можете просто использовать std::max, который принимает список инициализаторов.

Пример вызова:

auto maxNum = std::max({a, b, c});
person mkamerath    schedule 15.09.2020
comment
извините, но это не соответствует заявленному критерию (обеспечивает предполагаемую функциональность) - person Walter; 15.09.2020
comment
@Walter это не соответствует заявленному критерию Каким образом? - person Asteroids With Wings; 15.09.2020
comment
согласно en.cppreference.com/w/cpp/algorithm/max утверждает, что T must meet the requirements of CopyConstructible in order to use overloads (3,4). , и я думаю, что это причина того, что не используется перегрузка списка инициализаторов...... и, следовательно, комментарий Уолтера о некопируемых объектах;) - person Den-Jason; 16.09.2020
comment
@AsteroidsWithWings двумя способами: (1) я должен изменить max(a,b,c) везде в своем проекте - на данный момент это реализовано с помощью перегрузки, а не вариативного шаблона. (2) это не сработает для не копируемых объектов и создаст нежелательные копии больших объектов. - person Walter; 16.09.2020
comment
@Walter Ни один из этих критериев не был указан в вашем вопросе. - person Asteroids With Wings; 16.09.2020

Я бы предложил, чтобы ответ yao99 был принятым. Ради любопытства здесь я представляю вам решение, которое предоставляет адаптер через ваше собственное пространство имен. Это когда-либо использует только перегрузку двоичного параметра std::max, поэтому нет возможности вызвать перегрузку списка инициализатора. Код клиента по-прежнему может использовать max, как указано:

Адаптер:

#include <iostream>
#include <algorithm>

namespace variadicMax
{
    // the variadic "max" recurses and reduces to a binary parameter version that we must implement
    template<typename T>
    constexpr inline T const& max(T const&x, T const&y)
    {
        return (std::max(x, y));
    }

    // the variadic overload recurses itself, removing two parameters (and adding one) each time.
    // Eventually it reduces to (x,y) which is handled by the other overload.
    template<typename T, typename...Args>
    constexpr inline T const& max(T const&x, T const&y, Args&&...z)
    {
        return (max (std::max(x, y), std::forward<Args>(z)...));
    }
};

Клиент:

// this is the only change needed in clients:
using variadicMax::max;

int main()
{
    double x, y, z, w, v;

    x = 1;  y = 2;  z = 3;  w = 4;  v = 5;
    auto m5 = max(x, y, z, w, v);  std::cout << m5 << std::endl;
    auto m5A = max(x, y, z, 4.0, v);  std::cout << m5A << std::endl;

    x = 1;  y = 2;  z = 3;
    auto m = max(x, y, z);  std::cout << m << std::endl;

    x = 2;  y = 3;  z = 1;
    auto m2 = max(x, y, z);  std::cout << m2 << std::endl;

    x = 3;  y = 2;  z = 1;
    auto m3 = max(x, y, z);  std::cout << m3 << std::endl;

    x = 3;  y = 2;
    auto m4 = max(x, y);  std::cout << m4 << std::endl;

    x = 3;  y = 2;
    auto m6 = max(3, 2);  std::cout << m6 << std::endl;

    x = 3;  y = 2;
    auto m7 = max(x, 2.0);  std::cout << m7 << std::endl;

    etc...
person Den-Jason    schedule 16.09.2020