целое число -> правила преобразования указателя

19

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

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

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

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

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

user1334767
источник

Ответы:

18

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

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

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

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

грецкий орех
источник
-1

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

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

а также

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

а также

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

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

(1-1)представляет собой целое число со значением буквальным 0.

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

Damon
источник
1
Как целочисленный литерал ? Это выражение, содержащее два целочисленных литерала со значением и оператором. 1-11-
грецкий орех
@walnut: Вы, вероятно, ссылаетесь на неловкую формулировку «последовательность двоичных цифр, восьмеричных цифр, цифр или шестнадцатеричных цифр» . Это очень неудачная формулировка для чего-то довольно «очевидного», что предполагает что-то, что не соответствует действительности (то есть исключая знак минус). Только с «цифрами» и педантично в соответствии с определением «цифры» (один из 0 ... 9) невозможно иметь какие-либо отрицательные литералы (такие как -1). Что, поскольку тип по умолчанию подписан , однако, очевидно, необходимо, и это также очевидно (и общепризнанно).
Деймон
1
Я имею в виду грамматику для целочисленного литерала, показанную в стандартной ссылке, которая не соответствует 1-1. C ++ не имеет отрицательных целочисленных литералов. -1является выражением, состоящим из 1целочисленного литерала (типа со -знаком ) и унарного минусового оператора. Смотрите также раздел «Примечания» на cppreference.com .
грецкий орех
Конечно, в грамматике этого нет, но это совершенно несущественно. По необходимости и по определению, C ++ очень действительно есть негативные литералы, так как, если вы явно Append u, ваш буквальным есть, по определению, подписано. Подписанные типы имеют отрицательные значения (около 50% возможных значений являются отрицательными). К сожалению, грамматика (по причине, о которой я не знаю) вводит в заблуждение таким образом, и хотя технически (согласно грамматике) -1 является положительным буквальным значением, отрицается всеми другими средствами, конечно, является отрицательным буквальный. Так же, как 3 + 4 является буквальным.
Деймон
Кстати - я попробовал 0U. Та же проблема. То, что я не пробовал, является enumценностью. Возможно, названный человек изменил бы вещи. Я закончил тем, что написал длинное выражение с decltypeи remove_reference.
user1334767