Литералы и функции constexpr, оценка времени компиляции

Попытка реализовать приятный (простой, простой, без TMP, без макросов, без нечитаемого извилистого кода, без странного синтаксиса при его использовании) хэш времени компиляции с помощью пользовательских литералов, я обнаружил, что, очевидно, GCC понимает, что такое константное выражение: сильно отличается от моего понимания.

Поскольку вывод кода и компилятора говорят более тысячи слов, без лишних слов:

#include <cstdio>

constexpr unsigned int operator"" _djb(const char* const str, unsigned int len)
{
static_assert(__builtin_constant_p(str), "huh?");
return len ? str[0] + (33 * ::operator"" _djb(str+1, len-1)) : 5381;
}

int main()
{
printf("%u\n", "blah"_djb);
return 0;
}

Код довольно простой, не так много, чтобы объяснить, и не так много, чтобы спросить — за исключением того, что он не оценивает во время компиляции. Я попытался использовать разыменование указателя вместо использования индекса массива, а также прерывания рекурсии в !*str, все к тому же результату.

static_assert был добавлен позже, когда ловил рыбу в неспокойных водах, почему хэш просто не мог быть оценен во время компиляции, когда я твердо верил, что это должно быть. Что ж, удивительно, это только озадачило меня больше, но ничего не прояснило! Оригинальный код, без static_assert, хорошо принят и компилируется без предупреждений (gcc 4.7.2).

Выход компилятора:

[...]\main.cpp: In function 'constexpr unsigned int operator"" _djb(const char*, unsigned int)':
[...]\main.cpp:5:2: error: static assertion failed: huh?

Насколько я понимаю, строковый литерал, ну … буквальный. Другими словами, константа времени компиляции. В частности, это известная во время компиляции последовательность постоянных символов, начинающаяся с постоянного адреса, назначенного компилятором (и, таким образом, известного), оканчивающегося на '\0', Это логически подразумевает, что длина, вычисляемая компилятором литерала, предоставляется operator"" это constexpr также.

Кроме того, я понимаю, что вызов constexpr Функция только с параметрами времени компиляции делает ее элегантной в качестве инициализатора для перечисления или в качестве параметра шаблона, другими словами, это должно привести к вычислению во время компиляции.
Конечно в принципе всегда допустимый для компилятора, чтобы оценить constexpr функция во время выполнения, но возможность переместить оценку во время компиляции constexpr, в конце концов.

Где моя ошибка, и есть ли способ реализовать пользовательский литерал, который может принимать строковый литерал, чтобы он действительно вычислялся во время компиляции?

Возможно актуальные похожие вопросы:
Может ли строковый литерал быть подписан в константном выражении?
Пользовательские литеральные аргументы не являются constexpr?
Первый, кажется, предполагает, что по крайней мере для char const (&str)[N] это работает, и GCC принимает это, хотя я по общему признанию не могу следовать за заключением.
Второй использует целочисленные литералы, а не строковые литералы, и, наконец, решает проблему с помощью шаблонного метапрограммирования (что мне не нужно). Так что, видимо, проблема не ограничивается строковыми литералами?

2

Решение

У меня нет GCC 4.7.2, чтобы попробовать, но ваш код без статического утверждения (подробнее об этом позже) хорошо компилируется и выполняет функцию во время компиляции с обоими GCC 4.7.3 а также GCC 4.8. Я думаю, вам придется обновить свой компилятор.

Компилятору не всегда разрешается перемещать оценку во время выполнения: некоторые контексты, такие как аргументы шаблона, и static_assert, требует оценки во время компиляции или ошибка, если это невозможно. Если вы используете свой UDL в static_assert если это возможно, вы заставите компилятор оценить его во время компиляции. В обоих моих тестах это так.

Теперь, чтобы __builtin_constant_p(str), Для начала, как задокументировано, __builtin_constant_p может выдавать ложные негативы (то есть иногда может возвращать 0 для константных выражений).

str не является доказуемо константным выражением, потому что это аргумент функции. Вы можете заставить компилятор оценивать функцию во время компиляции в некоторых контекстах, но это не значит, что это может никогда оцените его во время выполнения: некоторые контексты никогда не вызывают принудительную оценку во время компиляции (и фактически, в некоторых из этих контекстов оценка во время компиляции просто невозможна). str может быть непостоянным выражением.

Статические утверждения проверяются, когда компилятор видит функцию, а не один раз для каждого вызова, который видит компилятор. Это делает тот факт, что вы всегда называете это в контексте времени компиляции, не имеет значения: имеет значение только тело. Так как str иногда может быть непостоянным выражением, __builtin_constant_p(str) в этом контексте не может быть правдой: он может производить ложные отрицания, но он не производит ложных срабатываний.

Чтобы было понятнее: static_assert(__builtin_constant_p("blah"), "") пройдет (ну, теоретически это может быть неудачей, но я сомневаюсь, что компилятор выдаст ложный отрицательный результат здесь), потому что "blah" является всегда постоянное выражение, но str не то же самое выражение, что и "blah",

Для полноты, если рассматриваемый аргумент был числового типа (подробнее об этом позже), и вы сделали тест вне статического утверждения, ты мог бы получить тест, чтобы вернуть истину если вы передали константу, и false, если вы передали не константу. В статическом утверждении это всегда терпит неудачу.

Но! Документы за __builtin_constant_p раскрыть одну интересную деталь:

Однако если вы используете его во встроенной функции и передаете аргумент функции в качестве аргумента встроенному, GCC никогда не вернет 1, когда вы вызываете встроенную функцию со строковой константой или составным литералом (см. Составные литералы) и не вернет 1, когда вы передадите постоянное числовое значение встроенной функции, если вы не укажете опцию -O.

Как видите, у встроенного есть ограничение делает тест всегда возвращаемым если данное выражение является строковой константой.

3

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]