целое число -> правила преобразования указателя - PullRequest
19 голосов
/ 24 марта 2020

Рассмотрим следующий код.

void f(double p) {}
void f(double* p) {}

int main()
{ f(1-1); return 0; }

MSV C 2017 не компилирует это. Он показывает, что существует неоднозначный перегруженный вызов, поскольку 1-1 совпадает с 0 и поэтому может быть преобразовано в double*. Другие приемы, такие как 0x0, 0L или static_cast<int>(0), также не работают. Даже объявление const int Zero = 0 и вызов f(Zero) приводит к той же ошибке. Это работает правильно, только если Zero не const.

Похоже, что та же проблема относится к G CC 5 и ниже, но не к G CC 6. Мне интересно, если это часть стандарта C ++, известная ошибка MSV C или настройка в компиляторе. Беглый Google не дал результатов.

Ответы [ 2 ]

18 голосов
/ 24 марта 2020

MSV C считает 1-1 константой нулевого указателя. Это было правильно по стандарту для C ++ 03, где все выражения целочисленных констант со значением 0 были константами нулевого указателя, но это было изменено так, что только нулевые целочисленные литералы являются константами нулевого указателя для C ++ 11 с CWG выпуск 903 . Это серьезное изменение, как вы можете видеть в своем примере и которое также задокументировано в стандарте, см. [diff.cpp03.conv] стандарта C ++ 14 (черновик N4140).

MSV C применяет это изменение только в режиме соответствия. Таким образом, ваш код будет компилироваться с флагом /permissive-, но я думаю, что изменение было реализовано только в MSV C 2019, см. здесь .

В случае G CC , G CC 5 по умолчанию работает в режиме C ++ 98, а G CC 6 и более поздние по умолчанию - в режиме C ++ 14, поэтому изменение поведения, похоже, зависит от версии G CC.

Если вы вызываете f с константой нулевого указателя в качестве аргумента, то вызов является неоднозначным, поскольку константа нулевого указателя может быть преобразована в значение нулевого указателя любого типа указателя, и это преобразование имеет тот же ранг, что и преобразование от int (или любого целочисленного типа) до double.

0 голосов
/ 24 марта 2020

Компилятор работает правильно, в соответствии с [over.match] и [conv] , более конкретно [conv.fpint] и [conv.ptr].

Стандартной последовательностью преобразования является [бла-бла] Ноль или одно [...] преобразование с плавающей запятой, преобразования указателя, [...].

и

Значение типа integer или типа перечисления с незаданной областью можно преобразовать в значение типа с плавающей запятой. Результат точен, если это возможно [бла-бла]

и

Константа нулевого указателя - это целочисленный литерал со значением ноль или [...]. Константа нулевого указателя может быть преобразована в тип указателя; результатом является значение нулевого указателя этого типа [бла-бла]

Теперь разрешение перегрузки состоит в том, чтобы выбрать лучшее соответствие среди всех функций-кандидатов (что, как забавная функция , даже не нужно быть доступным в месте вызова!). Лучшее совпадение - это то, которое имеет точные параметры или, с другой стороны, наименьшее количество возможных преобразований. Может произойти ноль или одно стандартное преобразование (... для каждого параметра), и ноль "лучше", чем единица.

(1-1) - это целочисленный литерал со значением 0.

Вы можете преобразовать нулевой целочисленный литерал в каждый из double или double* (или nullptr_t), с ровно одним преобразованием. Таким образом, предполагая, что объявлено более одной из этих функций (как в случае с примером), существует более одного кандидата, и все кандидаты одинаково хороши, лучшего совпадения не существует. Это неоднозначно, и компилятор вправе жаловаться.

...