Компилятор не может выполнить выражение 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 для всех значений объекта, одинаковых и известных во время компиляции)

Другие вопросы по тегам