Я пытаюсь вычислить длину строкового литерала во время компиляции. Для этого я использую следующий код:
#include <cstdio>
int constexpr length(const char* str)
{
return *str ? 1 + length(str + 1) : 0;
}
int main()
{
printf("%d %d", length("abcd"), length("abcdefgh"));
}
Все работает как положено, программа выводит 4 и 8. Ассемблерный код, сгенерированный clang, показывает, что результаты вычисляются во время компиляции:
0x100000f5e: leaq 0x35(%rip), %rdi ; "%d %d"
0x100000f65: movl $0x4, %esi
0x100000f6a: movl $0x8, %edx
0x100000f6f: xorl %eax, %eax
0x100000f71: callq 0x100000f7a ; symbol stub for: printf
Мой вопрос: гарантировано ли стандартом, что length
функция будет оцениваться во время компиляции?
Если это правда, дверь для вычислений строковых литералов времени компиляции только что открылась для меня ... например, я могу вычислять хэши во время компиляции и многое другое ...
<cstdio>
и звонки::printf
непереносимы. Стандарт требует только<cstdio>
предоставитьstd::printf
.printf
может привести к значительно меньшему количеству кода, с которым нужно работать.Ответы:
Не гарантируется, что постоянные выражения будут вычислены во время компиляции, у нас есть только ненормативная цитата из чернового стандартного раздела C ++
5.19
Постоянные выражения, в котором говорится следующее:Вы можете присвоить результат
constexpr
переменной, чтобы убедиться, что он оценивается во время компиляции, мы можем видеть это из справочника Бьярна Страуструпа по C ++ 11, в котором говорится ( выделено мной ):Например:
constexpr int len1 = length("abcd") ;
Бьярн Страуструп кратко описывает, когда мы можем гарантировать оценку времени компиляции в этой записи блога isocpp, и говорит:
Таким образом, здесь выделяются два случая, когда его следует оценивать во время компиляции:
shall be ... converted constant expression
илиshall be ... constant expression
, например, привязка к массиву.constexpr
как я обрисовал выше.источник
constexpr int x = 5;
, обратите внимание, что он не требует значения во время компиляции (при условии, что он не используется в качестве параметра шаблона или еще чего-то) и фактически генерирует код, который вычисляет начальное значение во время выполнения, используя 5 непосредственных значений 1 и 4 операций сложения. Более реалистичный пример: компилятор может достичь предела рекурсии и отложить вычисления до времени выполнения. Если вы не сделаете что-то, что заставляет компилятор фактически использовать значение, «гарантированная оценка во время компиляции» является проблемой QOI.constexpr
расчет из чистого зла. Можно даже подождать 1 секунду для каждого символа в заданной строке исходного текста или взять заданную строку исходного текста и использовать ее для заполнения шахматной позиции, а затем сыграть обеими сторонами, чтобы определить, кто победил.Очень легко узнать, приводит ли вызов
constexpr
функции к основному константному выражению или просто оптимизируется:Используйте его в контексте, где требуется постоянное выражение.
int main() { constexpr int test_const = length("abcd"); std::array<char,length("abcdefgh")> test_const2; }
источник
-pedantic
, если вы используете gcc. В противном случае вы не получите предупреждений и ошибокenum { Whatever = length("str") }
?static_assert(length("str") == 3, "");
constexpr auto test = /*...*/;
вероятно, самый общий и простой.Просто обратите внимание, что современные компиляторы (например, gcc-4.x) работают
strlen
со строковыми литералами во время компиляции, потому что они обычно определяются как встроенные функции . Без включенной оптимизации. Хотя результат не является постоянной времени компиляции.Например:
printf("%zu\n", strlen("abc"));
Результаты в:
movl $3, %esi # strlen("abc") movl $.LC0, %edi # "%zu\n" movl $0, %eax call printf
источник
strlen
это встроенная функция, если мы используем-fno-builtins
ее, она возвращается к вызову во время выполнения, посмотрите вживуюstrlen
этоconstexpr
для меня, даже с-fno-nonansi-builtins
(кажется,-fno-builtins
это в граммах не существует ++ больше). Я говорю "constexpr", потому что я могу это сделать,template<int> void foo();
иfoo<strlen("hi")>();
g ++ - 4.8.4Позвольте предложить другую функцию, которая вычисляет длину строки во время компиляции без рекурсии.
template< size_t N > constexpr size_t length( char const (&)[N] ) { return N-1; }
Взгляните на этот пример кода на ideone .
источник
char temp[256]; sprintf(temp, "%u", 2); if(1 != length(temp)) printf("Your solution doesn't work");
ideone.com/IfKUHVНет гарантии, что
constexpr
функция оценивается во время компиляции, хотя любой разумный компилятор выполнит это при включенных соответствующих уровнях оптимизации. С другой стороны, параметры шаблона должны оцениваться во время компиляции.Я использовал следующий трюк, чтобы принудительно выполнить оценку во время компиляции. К сожалению, он работает только с целыми значениями (т.е. не со значениями с плавающей запятой).
template<typename T, T V> struct static_eval { static constexpr T value = V; };
Теперь, если вы напишете
if (static_eval<int, length("hello, world")>::value > 7) { ... }
вы можете быть уверены, что этот
if
оператор является константой времени компиляции без дополнительных затрат времени выполнения.источник
len
какconstexpr
средстваlength
в любом случае должны оцениваться во время компиляции.if
-условие (где было необходимо, чтобы компилятор выполнял удаление мертвого кода), для которого я изначально использовал этот трюк.Краткое объяснение из статьи Википедии об обобщенных постоянных выражениях :
Наличие
constexpr
ключевого слова перед определением функции указывает компилятору проверить, соблюдены ли эти ограничения. Если да, и функция вызывается с константой, возвращаемое значение гарантированно будет постоянным и, таким образом, может использоваться везде, где требуется постоянное выражение.источник