Чи є різниця двох екземплярів constexpr __func__ покажчиків досі constexpr?


14

Чи дійсно це C ++?

int main() {
    constexpr auto sz = __func__ - __func__;
    return sz;
}

GCC та MSVC вважають, що це нормально, Кланг вважає, що це не так: Compiler Explorer .


Усі компілятори погоджуються, що це нормально: Провідник компілятора .

int main() {
    constexpr auto p = __func__;
    constexpr auto p2 = p;
    constexpr auto sz = p2 - p;
    return sz;
}

Клангу знову це не подобається, але з ним все в порядку: 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() це конспектр . Інші компілятори не скаржаться: Compiler Explorer


Кланг також не збиратиме цей. Скарги, що __func__ == __func__не є постійним виразом: Compiler Explorer

int main() {
    static_assert(__func__ == __func__);
}

З Function_definition , __func__це як-ніби static const char __func__[] = "function-name";і цей еквівалент приймається Демо ...
Jarod42

Цікаво, що це працює , якщо ви ініціалізувати змінну constexpr з __func__і використовувати його в static_assert ...
Флорестан

@ Jarod42 Отже, це помилка в Кланг?
Айксан

@florestan, як це ? Він також не компілюється з Кланг. Мої 2 та 3 приклади у питанні - це спосіб, який ви згадали. Одна компілює, а інша - ні.
Айксан

1
Див. Також CWG1962 , який може __func__повністю вилучатися з оцінки конспектора.
Оселедець Девіса

Відповіді:


13

__func__в C ++ - це ідентифікатор. Зокрема, він посилається на конкретний об’єкт. З [dcl.fct.def.general] / 8 :

Заздалегідь _­_­func_­_­задана змінна локальна функція визначається як би визначенням форми

static const char __func__[] = "function-name";

було надано, де ім'я функції - це рядок, визначений реалізацією. Не визначено, чи має така змінна адресу, відмінну від адреси будь-якого іншого об'єкта в програмі.

Як заздалегідь визначена змінна функція , це визначення (як би) з'являється на початку функціонального блоку. Таким чином, будь-яке використання __func__всередині цього блоку буде посилатися на цю змінну.

Що стосується частини "будь-який інший об'єкт", змінна визначає об'єкт. __func__називає об'єкт, визначений цією змінною. Тому в рамках функції всі використовують __func__імена однієї змінної. Невизначено, чи відрізняється ця змінна від інших об'єктів.

Тобто, якщо ви знаходитесь у функції з ім'ям foo, і ви використовували літерал "foo"десь ще в проблемі, не заборонено для реалізації, щоб змінна __func__також була тим самим об'єктом, який "foo"повертає літерал . Тобто стандарт не вимагає, щоб кожна функція, в якій __func__з'являється, зберігала дані окремо від самого буквеного рядка.

Тепер правило C ++ 'як ніби' дозволяє реалізаціям відхилятися від цього, але вони не можуть це зробити так, що було б виявлено. Отже, хоча сама змінна може мати або не мати відмінну адресу від інших об'єктів, використання __func__в тій самій функції повинно вести себе так, ніби вони посилаються на один і той же об'єкт.

Кланг, схоже, не реалізує __func__цей спосіб. Здається, реалізує його так, ніби він повернув первинний рядковий літерал імені функції. Дві чіткі літеральні рядки не повинні посилатися на один і той же об'єкт, тому віднімання покажчиків на них є UB. І невизначена поведінка в контексті постійного вираження є недобре сформованою.

Єдине, що змушує мене сказати, що Кланг тут на 100% помиляється, це [temp.arg.nontype] / 2 :

Для нетипового шаблону-параметра опорного чи вказівного типу значення константного виразу не повинно посилатися на (або для типу вказівника, не буде адресою):

...

  • заздалегідь визначена _­_­func_­_змінна.

Дивіться, це, здається, дозволяє реалізувати певні помилки впровадженням. Тобто, хоча __func__технічно це може бути постійним виразом, ви не можете використовувати його в параметрі шаблону. Це трактується як рядковий буквал, хоча технічно це змінна.

Отже, на якомусь рівні я б сказав, що стандарт говорить з обох боків рота.


Отже, строго кажучи __func__може бути постійним вираженням у всіх випадках мого питання, правда? Отже код повинен був скластись.
Айксан

Як щодо "Не визначено, чи така змінна має адресу, відмінну від адреси будь-якого іншого об'єкта в програмі". частина? Невизначена поведінка означає недетермінованість у поведінці абстрактної машини. Чи може це бути проблематично для оцінки contexpr? Що робити, якщо при першому виникненні __func__адреса така ж, як і в іншому об'єкті, а при другому виникнення __func__її немає? Зрозуміло, це не означає, що адреса відрізняється між двома примірниками, але я все ще плутаюся!
Йоханнес Шауб - ліб

@ JohannesSchaub-litb: " А як щодо" Не визначено, чи така змінна має адресу, відмінну від адреси будь-якого іншого об'єкта в програмі. Частина? " __func__не є макросом; це ідентифікатор, який називає конкретну змінну і, отже, конкретний об'єкт. Отже, будь-яке використання однієї і __func__тієї ж функції повинно спричинити glvalue, що посилається на один і той же об'єкт. Або, до речі, це не може бути реалізовано таким чином, щоб це було не так.
Нікол

@Nicol, який посилається на той самий об’єкт. Але цей об'єкт може в одну мить мати ту ж адресу, що й інший об’єкт. А в інший момент не зробили. Я не кажу, що це проблема, але я просто нагадую всім про таку можливість. І тоді, зрештою, я також можу помилитися, тому я також говорю це, сподіваючись виправити чи підтвердити.
Йоханнес Шауб - ліб

@ JohannesSchaub-litb: " Але цей об'єкт може в одну мить мати ту ж адресу, що й інший об'єкт ". Це не дозволяється в об'єктній моделі C ++. Два об'єкти, жоден з яких не вкладається в інший, не можуть обидва перебувати протягом свого життя в одному сховищі одночасно. І відповідний об'єкт має статичну тривалість зберігання, тому, якщо ви не використовуєте його розміщення new, він нікуди не дінеться, поки програма не закінчиться.
Нікол
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.