std :: max ведет себя неожиданно с равномерной инициализацией и constexpr int - PullRequest
5 голосов
/ 12 апреля 2020

Я экспериментирую с std :: max. Я пытаюсь передать целые числа constexpr с равномерной инициализацией (фигурные скобки), чтобы сравнить их с переменными с плавающей точкой.

Эксперимент а): вызов std :: max () с double / int mixed

    double a = 3.0;
    int b = 5;
    auto res = std::max(a, b);

Не компилируется. Clang сообщает error: no matching function for call to 'max'. Это, конечно, нормально.

Эксперимент b): используйте фигурные скобки + constexpr int для преобразования без ограничения

    double a = 3.0;
    constexpr int b = 5;
    auto res = std::max(a, {b});

Компилируется и работает, как ожидается: возвращает значение типа double со значением 5.0.

Эксперимент c): То же, что и b), но аргументы подстановки в std :: max.

    double a = 3.0;
    constexpr int b = 5;
    auto res = std::max({b}, a);

Не компилируются как в g cc, так и в clang. Почему?

Clang сообщает error: called object type 'double' is not a function or function pointer.

Ответы [ 2 ]

5 голосов
/ 12 апреля 2020

Существует перегрузка std::max, которая выглядит следующим образом (от cppreference.com ):

template< class T, class Compare >
constexpr T max( std::initializer_list<T> ilist, Compare comp );

Это лучше подходит для вашего звонка auto res = std::max({b}, a);, чем

template< class T >
constexpr const T& max( const T& a, const T& b );

, который вы пытаетесь вызвать, так как {b} может быть выведен на std::initializer_list<int>, и этот вызов имеет ранг преобразования с точным соответствием в обоих аргументах, в то время как перегрузка, которую вы хотите вызвать, требует преобразования от int до double, что не является точным соответствием.

Второй аргумент тогда считается функтором Compare, который вызывается для операции сравнения, но вызов double явно не удался. Перегрузка не отключается, если второй аргумент не вызывается, поэтому он все еще выбран.

Этого не происходит с auto res = std::max(a, {b});, потому что нет перегрузки с параметром std::initializer_list для второго аргумент и так только перегрузка, которую вы хотите вызвать, является жизнеспособным. Список инициализаторов делает второй параметр не выводимым контекстом, поэтому он работает в отличие от auto res = std::max(a, b);, который завершается ошибкой из-за несоответствия вывода аргументов шаблона между двумя аргументами.

0 голосов
/ 12 апреля 2020

В третьем «эксперименте» у вас есть список инициализаторов в качестве первого аргумента std::max; таким образом, компилятор пытается использовать следующий шаблон (см. cppreference) :

template< class T, class Compare >
T max( std::initializer_list<T> ilist, Compare comp );

, в котором второй аргумент должен быть функцией сравнения.

...