Компилятор не может выполнить выражение constexpr
У меня есть примерно такой код:
template<typename ... Args>
constexpr size_t get_init_size(Args ... args) {
return sizeof...(Args);
}
template<typename ... Args>
constexpr auto make_generic_header(Args ... args) {
constexpr size_t header_lenght = get_init_size(args...);
return header_lenght;
}
constexpr auto create_ipv4_header() {
constexpr auto x = make_generic_header(0b01, 0b10, 0b01);
return x;
}
Я знаю, что это фиктивный код, но я изолирую его, чтобы найти ошибку.
Компилятор выдает мне ошибку (GCC):
In instantiation of 'constexpr auto make_generic_header(Args&& ...) [with Args = {int, int, int}]':
/tmp/tmp.CaO5YHcqd8/network.h:39:43: required from here
/tmp/tmp.CaO5YHcqd8/network.h:31:22: error: 'args#0' is not a constant expression
31 | constexpr size_t header_lenght = get_init_size(args...);
| ^~~~~~~~~~~~~
Я попытался добавить квалификатор const в параметры функции, но это не сработало. Теоретически все эти функции могут вычисляться во время компиляции. Но где проблема, я не могу найти со своими знаниями.
3 ответа
означает разные вещи для переменных и функций.
Для переменных это означает, что переменная должна быть во время компиляции. Поэтому его необходимо инициализировать константным выражением.
Для функций это означает, что функция может быть запущена во время компиляции в дополнение к среде выполнения с использованием того же кода внутри. Следовательно, все, что вы делаете внутри, должно быть применимо и к вызову во время выполнения.
Имея это в виду, давайте изучим:
template<typename ... Args>
constexpr auto make_generic_header(Args ... args) {
constexpr size_t header_lenght = get_init_size(args...);
return header_lenght;
}
Здесь переменная, поэтому должно быть время компиляции. Следовательно,
get_init_size(args...)
также должно выполняться во время компиляции. Однако параметры не являются константными выражениями по разным причинам, так что это не сработает. Если бы это сработало, это означало бы, что
make_generic_header
, функция, непригодна для использования во время выполнения¹, что не соответствует ее требованию использования как во время компиляции, так и во время выполнения.
Исправление довольно простое: используйте код, который работает в обоих случаях:
template<typename ... Args>
constexpr auto make_generic_header(Args ... args) {
size_t header_lenght = get_init_size(args...);
return header_lenght;
}
Вы это заметили? Единственным изменением является удаление из
header_lenght
. Если эта функция запускается во время компиляции, она все равно будет работать, и общее выражение вызова функции будет константным выражением.
¹"Но это не будет работать в
consteval
ни!" - Правда, рассуждений, приведенных в ответе, достаточно для
constexpr
, но я не упомянул более фундаментальную причину, так как она здесь неуместна.
Дело не в справочной проблеме. А переменная и функция - разные вещи. Цитата из ответа /questions/45775412/c14-initsializatsiya-peremennyih-constexpr-iz-znachenij-parametrov/45775436#45775436:
Ссылка не имеет предшествующей инициализации с точки зрения i: это параметр. Он инициализируется после вызова ByReference.
Это нормально, так как у f есть предшествующая инициализация. Инициализатор f также является константным выражением, поскольку неявно объявленный конструктор по умолчанию в данном случае является constexpr (§12.1/5). Следовательно, i инициализируется константным выражением, а вызов сам по себе является константным выражением.
И о «предшествующей инициализации», приведенной здесь :
Это означает «быть инициализированным», но более важно, чтобы видимость предшествующей инициализации в контексте оцениваемого выражения. В вашем примере в контексте оценки func(0) компилятор имеет контекст, чтобы увидеть инициализацию rf с 0. Однако в контексте оценки только выражения rf внутри func он не видит инициализацию rf . Анализ является локальным, так как он не анализирует каждый сайт вызова. Это приводит к тому, что само выражение rf в func не является константным выражением, в то время как func(0) является константным выражением.
В соответствии с вашим случаем строка:
constexpr size_t header_length = get_init_size(args...);
С точки зрения,
get_init_size(args...)
не является основным постоянным выражением, поскольку его аргумент вызова берется из аргумента функции, и args также не является основным постоянным выражением.
Простая модификация может заставить ваш код работать, вы можете удалить
constexpr
определитель из
header_length
, или напрямую вернуться
get_init_size(args...);
в
make_generic_header
тело функции .
Я переписываю код, который будет работать, и я думаю, что он будет выполняться во время компиляции:
template<typename ... Args>
constexpr auto make_generic_header(const Args ... args) {
std::integral_constant<size_t, sizeof...(Args)> header_lenght;
return header_lenght.value;
}
constexpr auto create_ipv4_header() {
constexpr auto x = make_generic_header(0b01, 0b10, 0b01);
return x;
}
я удалил функцию
get_init_size
и используемая кодовая часть параметра шаблона (гарантируется, что он будет выполняться во время компиляции) и после возврата количества аргументов, переданных в функцию (для std::integral_constant для всех значений объекта, одинаковых и известных во время компиляции)