Почему эти две части кода с использованием constexpr, __PRETTY_FUNCTION__ и char * имеют разные результаты?

У меня есть этот код, где, если вы прокомментируете строку, прокомментированную "Но это не работает?!" он компилируется просто отлично, но если вы этого не сделаете, компилятор генерирует ошибку.

По крайней мере, gcc 8.2 генерирует ошибку.

Но они кажутся мне идентичными. В чем проблема? Является ли этот правовой код вообще?

template <int x>
struct test_template {
    static int size() { return x; }
};

constexpr int ce_strlen(char const *s)
{
    int i = 0;
    while (s[i]) ++i;
    return i;
}

int joe()
{
    constexpr int plen = ce_strlen(__PRETTY_FUNCTION__); // This works
    test_template<plen> a; // This declaration is valid.
    test_template<ce_strlen(__PRETTY_FUNCTION__)> b; // But this doesn't work?!
    return a.size() + b.size();
}

Я столкнулся с этим, пытаясь придумать способ создания тегов профиля для интрузивной системы профилирования во время компиляции. Мне это удалось, но мой последний код не включает использование ce_strlen.

Ответ 1

Действительно, это ошибка в GCC, как обсуждалось в комментариях, но я подумал, что я бы добавил некоторые дополнительные сведения о природе этой ошибки. В файле новостей GCC есть следующая строка:

  • __FUNCTION__ и __PRETTY_FUNCTION__ теперь рассматриваются как переменные с помощью синтаксического анализатора; ранее они рассматривались как строковые константы. Поэтому код, например printf (__FUNCTION__ ": foo") должен быть переписан на printf ("%s: foo", __FUNCTION__). Это необходимо для шаблонов.

Но __PRETTY_FUNCTION__ самом деле не является переменной, это особый случай, рассматриваемый в синтаксическом анализаторе, как мы видим в constexpr.c:

    case DECL_EXPR:
      {
    tree decl = DECL_EXPR_DECL (body);
    if (TREE_CODE (decl) == USING_DECL
        /* Accept __func__, __FUNCTION__, and __PRETTY_FUNCTION__.  */
        || DECL_ARTIFICIAL (decl))
      return NULL_TREE;
    return error_mark_node;
}

Если бы это была переменная, мы ожидаем, что она пройдет те же тестовые примеры, что и они:

constexpr const char* s2 = "TEST";
constexpr const char* s3 = s2;
test_template<ce_strlen("TEST")> c;
test_template<ce_strlen(s2)> d;
test_template<ce_strlen(s3)> e;