Этот код C ++, возможно, на удивление, печатает 1
,
#include <iostream>
std::string x();
int main() {
std::cout << "x: " << x << std::endl;
return 0;
}
x
является прототипом функции, который, кажется, рассматривается как указатель на функцию, и в разделе 4.12 «Логические преобразования» стандарта C ++ говорится:
4.12 Булевы преобразования [conv.bool] 1 Значение арифметики, перечисления с незаданной областью, указателя или указателя на тип члена может быть
преобразован в prvalue типа bool. Нулевое значение, нулевое значение указателя,
или значение указателя нулевого члена преобразуется в false; любое другое значение
преобразуется в истину. Для прямой инициализации (8.5), значение типа
std :: nullptr_t может быть преобразован в значение типа bool;
результирующее значение ложно.
Тем не мение, x
никогда не привязан к функции. Как и следовало ожидать, компоновщик C не позволяет этого. Однако в C ++ это совсем не проблема. Кто-нибудь может объяснить это поведение?
Здесь происходит то, что указатель функции неявно преобразуется в bool
, Это указано [conv.bool]
:
Нулевое значение, нулевое значение указателя или нулевое значение указателя члена преобразуется в
false
;
любое другое значение преобразуется вtrue
где «значение нулевого указателя» включает указатели нулевой функции. Поскольку указатель на функцию, полученный в результате распада имени функции, не может быть нулевым, это дает true
, Вы можете увидеть это, включив << std::boolalpha
в выходной команде.
Следующее вызывает ошибку ссылки в g ++: (int)x;
Относительно того, разрешено это поведение или нет, C ++ 14 [basic.odr.ref]/3
говорит:
Функция, имя которой появляется в качестве потенциально вычисляемого выражения, используется в odr, если она
уникальный результат поиска или выбранный член набора перегруженных функций […]
который охватывает этот случай, так как x
в выходном выражении ищется до объявления x
выше, и это уникальный результат. Затем в /4
у нас есть:
Каждая программа должна содержать ровно одно определение каждой не встроенной функции или переменной, которая используется в этой программе в виде odr; Диагностика не требуется.
таким образом, программа плохо сформирована, но диагностика не требуется, а это означает, что поведение программы полностью не определено.
Кстати, этот пункт подразумевает, что для ссылки не требуется ошибка x();
либо, однако, с точки зрения качества реализации; это было бы глупо. Конечно, что g++
выбрал здесь, кажется разумным для меня.
X
не нужно быть «привязанным» к функции, потому что вы указали в своем коде, что такая функция существует. Таким образом, компилятор может смело предположить, что адрес этой функции не должен быть NULL. Чтобы это было возможно, вы должны объявить функцию слабым символом, а вы этого не сделали. Линкер не протестовал, потому что вы никогда не вызываете свою функцию (вы никогда не используете ее фактический адрес), поэтому он не видит проблем.
Функция, имя которой появляется
в качестве потенциально оцениваемого выражения используется odr, если оно является уникальным результатом поиска или выбранным членом
набор перегруженных функций (3.4, 13.3, 13.4), если это не чисто виртуальная функция и ее имя не является явным
Квалифицированный.
Следовательно, строго говоря, код использует функцию odr и поэтому требует определения.
Но современные компиляторы поймут, что точный адрес функции на самом деле не имеет отношения к поведению программы, и, таким образом, исключит использование и не потребует определения.
Также обратите внимание, что [basic.def.odr] / 3 указывает:
Каждая программа должна содержать ровно одно определение каждого не встроенного
функция или переменная, которая используется в этой программе; нет диагностики
требуется.
Реализация не обязана останавливать компиляцию и выдавать сообщение об ошибке (= диагностика). Он может делать то, что считает лучшим. Другими словами, любое действие разрешено, и у нас есть UB.