Это действительный C ++?
int main() {
constexpr auto sz = __func__ - __func__;
return sz;
}
GCC и MSVC считают, что все в порядке, Clang считает, что это не так: Compiler Explorer .
Все компиляторы соглашаются, что с этим все в порядке: Compiler Explorer .
int main() {
constexpr auto p = __func__;
constexpr auto p2 = p;
constexpr auto sz = p2 - p;
return sz;
}
Clang снова не нравится этот, но другие в порядке с этим: Compiler Explorer
int main() {
constexpr auto p = __func__;
constexpr auto p2 = __func__;
constexpr auto sz = p2 - p;
return sz;
}
Что здесь? Я думаю, что арифметика на несвязанных указателях - неопределенное поведение, но __func__
возвращает тот же самый указатель, нет? Я не уверен, поэтому я подумал, что могу это проверить. Если я правильно помню, std::equal_to
можно сравнить несвязанные указатели без неопределенного поведения:
#include <functional>
int main() {
constexpr std::equal_to<const char*> eq{};
static_assert(eq(__func__, __func__));
}
Кланг считает, что eq(__func__, __func__)
это не постоянное выражение, хотя std::equal_to::operator()
является constexpr . Другие компиляторы не жалуются: Compiler Explorer
Clang тоже не скомпилирует. Жалуется, что __func__ == __func__
не является константным выражением: Compiler Explorer
int main() {
static_assert(__func__ == __func__);
}
__func__
как если бы,static const char __func__[] = "function-name";
и этот эквивалент принимается Демо ...__func__
и используете ее в static_assert ...__func__
полностью удалить из оценки constexpr.Ответы:
__func__
в С ++ есть идентификатор. В частности, он ссылается на конкретный объект. Из [dcl.fct.def.general] / 8 :Как локальная предопределенная переменная функции , это определение (как будто) появляется в начале функционального блока. Таким образом, любое использование
__func__
внутри этого блока будет ссылаться на эту переменную.Что касается части «любой другой объект», переменная определяет объект.
__func__
называет объект, определенный этой переменной. Следовательно, внутри функции все__func__
имена используют одну и ту же переменную. Не определено, является ли эта переменная отличным от других объектов.То есть, если вы находитесь в функции с именем
foo
, и вы использовали литерал"foo"
где-то еще в проблеме, для реализации не запрещено, чтобы переменная__func__
также была тем же объектом, который"foo"
возвращает литерал . То есть стандарт не требует, чтобы каждая функция, которая__func__
появляется, должна хранить данные отдельно от строкового литерала.Теперь правило C ++ «как будто» позволяет реализациям отклоняться от этого, но они не могут сделать это так, чтобы это можно было обнаружить. Таким образом, хотя сама переменная может иметь или не иметь адрес, отличный от других объектов, использование одной и
__func__
той же функции должно вести себя так, как если бы они ссылались на один и тот же объект.Clang, похоже, не реализует
__func__
этот способ. Похоже, он реализован так, как будто он возвратил строковый литерал prvalue имени функции. Два разных строковых литерала не должны ссылаться на один и тот же объект, поэтому вычитать указатели на них - это UB. И неопределенное поведение в контексте постоянного выражения плохо сформировано.Единственное, что заставляет меня сомневаться в том, что Clang на 100% ошибочен, это [temp.arg.nontype] / 2 :
Видите, это, кажется, позволяет немного обмануть реализацию. То есть, хотя
__func__
технически может быть константным выражением, его нельзя использовать в параметре шаблона. Он обрабатывается как строковый литерал, хотя технически он является переменной.Так что на каком-то уровне я бы сказал, что стандарт говорит с обеих сторон.
источник
__func__
может быть постоянным выражением во всех случаях в моем вопросе, верно? Так что код должен был скомпилироваться.__func__
адрес совпадает с адресом другого объекта, а во втором__func__
- нет? Конечно, это не означает, что адрес отличается между двумя экземплярами, но я все еще в замешательстве!__func__
не макрос; это идентификатор, который называет конкретную переменную и, следовательно, конкретный объект. Следовательно, любое использование одной и__func__
той же функции должно приводить к glvalue, который ссылается на тот же объект. Или, что более важно, это не может быть реализовано таким образом, чтобы это было не так.new
на нем, он никуда не денется, пока программа не завершится.