__PRETTY_FUNCTION__ в постоянном выражении - PullRequest
10 голосов
/ 25 апреля 2019

Пожалуйста, обратитесь к этому фрагменту:

#include <type_traits>
#include <string_view>

constexpr std::size_t strlen(char const* s) {
    std::size_t n = 0;
    while (*s++ != '\0')
        ++n;
    return n;
}

template <std::size_t>
struct X {};

int main() {
    constexpr auto pf = __PRETTY_FUNCTION__; // gcc ok; clang ok; (1)
    static_assert(std::string_view(__PRETTY_FUNCTION__) == std::string_view("int main()")); // gcc ok; clang ok; (2)
    X<strlen(__PRETTY_FUNCTION__)> x; // gcc not ok; clang ok; (3)
}

Clang 8 компилирует его, но GCC 8.3 не делает.Смотри на Годболт .Сбой GCC на линии (3), хотя строки (1) и (2) в порядке.Если я могу объявить pf в строке (1) и использовать __PRETTY_FUNCTION__ в static_assert, это означает, что выражение __PRETTY_FUNCTION__ является выражением основной константы.И если я не могу объявить X<strlen(__PRETTY_FUNCTION__)> x, это означает, что strlen(__PRETTY_FUNCTION__) не является выражением с интегральной константой.

Так почему strlen(__PRETTY_FUNCTION__) не является выражением с интегральной константой?Это подразумевается стандартом, или это ошибка GCC?

Ответы [ 2 ]

5 голосов
/ 25 апреля 2019

__PRETTY_FUNCTION__ не стандартно.Таким образом, компилятор может реализовать его в разных местах (при синтаксическом анализе, при создании AST или при компоновке).

Если он должен быть реализован при синтаксическом анализе, то он может быть константным выражением (думаю, это то, чтолязг делает).Однако, если он реализован во время компоновки (то есть компилятор выдает для него символ, а компоновщик разрешит его), он не может быть константным выражением.

Я думаю, что GCC использует последний случай.

Обратите внимание, что в этом случае вы можете использовать их sizeof (), так как это const char[], если вам нужно вычислить длину строки констант во время компиляции.Поэтому замените выражение 3 на:

X<sizeof(__PRETTY_FUNCTION__) - 1> x;

, и оно прекрасно скомпилируется на обоих компиляторах.

РЕДАКТИРОВАТЬ: Как отметил Натан Оливер, похоже, что GCC рассматривает подпись __PRETTY_FUNCTION__ как static const char[], в то время как clang / visual studio рассматривает ее как static constexpr const char[].Это болезненная неприятность в GCC (не ошибка, поскольку она не является стандартной), и они, кажется, исправили ее в версии> 8.0.0.

В выражениях (1) и выражении (2), __PRETTY_FUNCTION__ уменьшается до const char* (указатель постоянный, но ничего не скажешь о данных).Для меня выражение (2) может ничего не доказывать, поскольку нет никакой гарантии, что указатели должны быть равными с обеих сторон равенства, даже если они указывают на «одинаковое» содержимое.string_view конструктор ожидает const char*, поэтому все, кроме __PRETTY_FUNCTION__, которое может распасться на const char*, передало бы выражение (2).

0 голосов
/ 26 апреля 2019

Меня по-прежнему беспокоит строка (2), поэтому я понял, что не вписывается в комментарий.

Я немного изменил фрагмент, пожалуйста, обратитесь к этому:

#include <type_traits>
#include <string_view>

constexpr std::size_t strlen(char const* s) {
    std::size_t n = 0;
    while (*s++ != '\0')
        ++n;
    return n;
}

template <std::size_t>
struct X {};

static char const PF[] = "int main()";

int main() {
    constexpr auto pf = std::string_view(__PRETTY_FUNCTION__); // gcc ok; clang ok; (1)
    static_assert(pf == std::string_view("int main()")); // gcc ok; clang ok; (2)
    X<strlen(__PRETTY_FUNCTION__)> x; // gcc not ok; clang ok; (3)

    static_assert(pf[0] == 'i'); // not ok; (4)
    X<pf.size()> x1; // ok; (5)
    X<strlen(pf.data())> x2; // not ok; (6)

    static_assert(__builtin_constant_p(pf.data()) == 0); // ok (7)
    static_assert(__builtin_strlen(pf.data()) == 10); // ok (8)
    static_assert(__builtin_memcmp(pf.data(), "int main()", __builtin_strlen(pf.data())) == 0); // ok (9)
    static_assert(std::char_traits<char>::compare(pf.data(), "int main()", std::char_traits<char>::length(pf.data())) == 0); // ok (10)

    static_assert(std::char_traits<char>::length(PF) == 10); // not ok (11)
    static_assert(__builtin_strlen(PF) == 10); // not ok (12)
}

Как я понимаю, static_assert не сможет доказать значение (2), если оно не believe, выражение было constexpr (как в случае строки (4)).Но, несмотря на неисправные линии (2) и (4), линия (5) кажется ОК для GCC.Итак, я заглянул в std::char_traits<char>.Черты используют __builtin_constant_p для распределения между реализациями __builtin_* и такими, как у меня strlen.В строке (7) указано, что «консистентность __PRETTY_FUNCTION__ не может быть доказана» (см. gcc doc ), но, несмотря на это, выражение __builtin_memcmp(__PRETTY_FUNCTION__) может быть оценено во время компиляции (см. Строку(8)).

Принимая во внимание несоответствие в (11) и (12), можно сделать вывод, что в некоторых случаях __PRETTY_FUNCTION__ действует так, как если бы он был объявлен static constexpr char const [], а в других случаях - static char const [].Другими словами, если __PRETTY_FUNCTION__ имеет тип, то он не согласован на всех этапах компиляции (я ссылаюсь на GCC 8.3).

...