Вычисление длины строки C во время компиляции. Это действительно констебр?
Я пытаюсь вычислить длину строкового литерала во время компиляции. Для этого я использую следующий код:
#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
будет оцениваться временем компиляции?
Если это правда, то дверь для компиляции времени строковых литералов вычислений только открылась для меня... например, я могу вычислить хэши во время компиляции и многое другое...
Ответы
Ответ 1
Константные выражения не гарантируются для оценки во время компиляции, у нас есть только ненормативная цитата из проекта стандарта С++ 5.19
Константные выражения, которые говорят об этом хотя:
[...] > [Примечание: константные выражения могут быть оценены во время перевод.-end note]
Вы можете присвоить результат переменной constexpr
, чтобы убедиться, что она оценивается во время компиляции, мы можем видеть это из ссылки Bjarne Stroustrup С++ 11, которая говорит (внимание мое):
Помимо возможности оценивать выражения во время компиляции, мы хотите иметь возможность требовать выражения, подлежащие оценке при компиляции время; constexpr перед определением переменной делает это (и означает const):
Например:
constexpr int len1 = length("abcd") ;
Bjarne Stroustrup дает краткое изложение того, когда мы можем заверить оценку времени компиляции в этой записи isocpp и говорит:
[...] Правильный ответ - как указано Herb - это то, что согласно стандарту функция constexpr может оцениваться во время компиляции или времени выполнения, если оно не используется как постоянное выражение, и в этом случае оно должно быть оценено в время компиляции. Чтобы гарантировать оценку времени компиляции, мы должны либо использовать где требуется постоянное выражение (например, в качестве привязки массива или как ярлык case) или использовать его для инициализации constexpr. Я бы надеялся что ни один уважающий себя компилятор не упустит оптимизацию возможность сделать то, что я изначально сказал: "Функция constexpr оценивается во время компиляции, если все его аргументы постоянны Выражения".
Итак, это описывает два случая, когда их следует оценивать во время компиляции:
- Используйте его там, где требуется постоянное выражение, это может быть где угодно в проекте стандарта, где используется фраза
shall be ... converted constant expression
или shall be ... constant expression
, такая как привязка к массиву.
- Используйте его для инициализации
constexpr
, как я выше описывал.
Ответ 2
Очень легко выяснить, приводит ли вызов к функции constexpr
к основному постоянному выражению или просто оптимизируется:
Используйте его в контексте, где требуется постоянное выражение.
int main()
{
constexpr int test_const = length("abcd");
std::array<char,length("abcdefgh")> test_const2;
}
Ответ 3
Просто обратите внимание, что современные компиляторы (например, gcc-4.x) выполняют strlen
для строковых литералов во время компиляции, потому что они обычно определяются как внутренняя функция. Без оптимизации. Хотя результат не является постоянной времени компиляции.
например:.
printf("%zu\n", strlen("abc"));
Результаты в:
movl $3, %esi # strlen("abc")
movl $.LC0, %edi # "%zu\n"
movl $0, %eax
call printf
Ответ 4
Позвольте мне предложить другую функцию, которая вычисляет длину строки во время компиляции, не будучи рекурсивной.
template< size_t N >
constexpr size_t length( char const (&)[N] )
{
return N-1;
}
Посмотрите на этот образец кода на ideone.
Ответ 5
Нет гарантии, что функция constexpr
оценивается во время компиляции, хотя любой разумный компилятор сделает это на соответствующих уровнях оптимизации. С другой стороны, параметры шаблона должны оцениваться во время компиляции.
Я использовал следующий трюк для принудительной оценки во время компиляции. К сожалению, он работает только со встроенными значениями (т.е. Не с плавающей запятой).
template<typename T, T V>
struct static_eval
{
static constexpr T value = V;
};
Теперь, если вы пишете
if (static_eval<int, length("hello, world")>::value > 7) { ... }
вы можете быть уверены, что оператор if
- это константа времени компиляции без накладных расходов во время выполнения.
Ответ 6
Краткое объяснение из статьи в Википедии Обобщенные константные выражения:
Использование constexpr для функции накладывает некоторые ограничения на то, что может выполнять эта функция. Во-первых, функция должна иметь непустой тип возврата. Во-вторых, тело функции не может объявлять переменные или определять новые типы. В-третьих, тело может содержать только декларации, нулевые утверждения и один оператор возврата. Должны существовать значения аргументов, так что после замены аргумента выражение в операторе return выражает константное выражение.
Имея ключевое слово constexpr
перед определением функции, инструктирует компилятор проверить, соблюдены ли эти ограничения. Если да, и функция вызывается с константой, возвращаемое значение гарантировано будет постоянным и, следовательно, может использоваться везде, где требуется постоянное выражение.