Почему параметры шаблона не считаются постоянными выражениями (для consteval)?

Я хотел бы использовать функции consteval для выполнения вычислений над параметрами шаблона во время компиляции, чтобы мои операции приводили к немедленным результатам, которые не влекут за собой никаких потерь производительности. Например:

// Changing this to constexpr will cause the function
// to be invoked at runtime.
consteval int abs(int x) {
    return x < 0 ? -x : x;
}

template <int N>
struct Test {
    bool isValid(int y) {
        return y < abs(N); // Error.
    }
};

Я использовал consteval, чтобы abs(N) было заменено немедленным значением, а не вызывать функцию во время выполнения (как это произошло бы, если бы abs() было constexpr). Например, я думаю, что внутри тела Test<7>::isValid()abs(7) разрешается до 7 и его не нужно вычислять во время выполнения. Однако этот код выдает следующую ошибку:

Вызов функции consteval 'abs' не является постоянным выражением... подвыражение недопустимо в константном выражении [invalid_consteval_call]

(проверено в clang 17.0.6). Эта ошибка по-прежнему возникает, если вы пытаетесь определить переменную static constexpr на основе N:

// Changing this to constexpr works though!
consteval int abs(int x) {
    return x < 0 ? -x : x;
}

template <int N>
struct Test {
    static constexpr int ABS_N = abs(N); // Error

    bool isValid(int y) {
        return y < ABS_N;
    }
};

Самое смешное, что если вы измените abs() с consteval на constexpr в этой версии, то сгенерированный код будет использовать немедленный код, как я и хотел! С другой стороны, шаблоны можно использовать с consteval для достижения той же цели:

template <int X>
consteval int abs() {
    return X < 0 ? -X : X;
}

template <int N>
struct Test {
    bool isValid(int y) {
        return y < abs<N>();
    }
}

Я также заметил, что использование переменной static constexpr вместо параметра шаблона работает, хотя здесь довольно легко понять, почему:

struct Test {
    static constexpr int N = 7;

    bool isValid(int y) {
        return y < abs(N);
    }
}

Почему параметры шаблона нельзя передавать в функции consteval?

Параметры функции не являются постоянными выражениями. Если бы они были, вы могли бы создать функцию, которая возвращает разные типы в зависимости от значения параметра. Это не то, что язык может поддерживать.

NathanOliver 26.04.2024 00:19

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

Cem 26.04.2024 00:33

дубликаты связаны с использованием параметра функции в качестве аргумента шаблона. Этот вопрос об обратном

463035818_is_not_an_ai 26.04.2024 08:56

@ 463035818_is_not_an_ai Это не меняет того факта, что параметры функции, такие как x в consteval int abs(int x); , не являются постоянными выражениями.

user12002570 26.04.2024 09:13

@user12002570 user12002570 «почему не параметры шаблона ...» против «параметры функции, такие как x ... не являются постоянными выражениями», один касается параметров шаблона, другой — параметров функции. В ходе этого вопроса также упоминается вопрос о параметрах функции, но исходный код и вопрос касаются параметра шаблона, используемого для вызова функции.

463035818_is_not_an_ai 26.04.2024 09:16
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
153
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ваша функция consteval имеет неопределенное поведение, если x равно INT_MIN (также известно как std::numeric_limits<int>::min()).

Этот код исправляет это:

#include <limits>
consteval int abs(const int x) {
    if (x == std::numeric_limits<int>::min())
        return std::numeric_limits<int>::max();
    return x < 0 ? -x : x;
}

Только благодаря этому изменению мне не удалось воспроизвести вашу ошибку в clang (trunk = 18.1.0 на x64).

Однако ваш метод isValid(), вероятно, должен быть static или, по крайней мере, const, то есть:

#include <limits>

consteval int abs(const int x) {
    if (x == std::numeric_limits<int>::min())
        return std::numeric_limits<int>::max();
    return x < 0 ? -x : x;
}

template <int N>
struct Test {
    static constexpr int ABS_N = abs(N); // Should be fine.

    static constexpr bool isValid(const int y) noexcept {
        return y < ABS_N;
    }
};

Прямая трансляция в Compiler Explorer

но почему код компилируется без ошибок и без применения вашего исправления? godbolt.org/z/fYhjGoKeW

463035818_is_not_an_ai 26.04.2024 09:33

Ах, да. Забыл сказать, в чем заключалось исправление. consteval (или constexpr) делает вызов функции постоянным выражением. Проверка на переполнение делает код действительным, даже если функция constexpr (неопределенное поведение не допускается с помощью consteval)

viraltaco_ 27.04.2024 13:21

На самом деле ваша проблема связана с конфликтом имен: (не consteval) abs из заголовка C конфликтует с вашим abs.

Переименование вашей функции решает проблему:

consteval int my_abs(int x) {
    return x < 0 ? -x : x;
}

template <int N>
struct Test {
    static constexpr int ABS_N = my_abs(N); // OK
    bool isValid(int y) {
        return y < my_abs(N); // OK
    }
};

int main() {
    Test<42>().isValid(0);
}

Демо

Однако если бы это определение было противоречивым, оно бы не сработало при замене consteval на constexpr. Будет ли это?

viraltaco_ 27.04.2024 13:42

Для меня это не так... Мы заявляем, что то, что хочет ОП, возможно, но не удалось воспроизвести точную проблему ОП.

Jarod42 27.04.2024 13:49
Ответ принят как подходящий

Похоже, что моя система сборки использовала установленную в системе версию clang (14.0.0), а не ту, которую я установил через Homebrew. Компиляция вручную с помощью clang 18.1.4 (после обновления) работала нормально. Спасибо @Cem за тестирование различных версий компилятора и указание мне правильного направления!

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