Я хотел бы использовать функции 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?
Пожалуйста, опубликуйте минимальный воспроизводимый пример. Шаблон в начале компилировался нормально со всеми компиляторами, которые я пробовал и мог вызвать isValid().
дубликаты связаны с использованием параметра функции в качестве аргумента шаблона. Этот вопрос об обратном
@ 463035818_is_not_an_ai Это не меняет того факта, что параметры функции, такие как x в consteval int abs(int x); , не являются постоянными выражениями.
@user12002570 user12002570 «почему не параметры шаблона ...» против «параметры функции, такие как x ... не являются постоянными выражениями», один касается параметров шаблона, другой — параметров функции. В ходе этого вопроса также упоминается вопрос о параметрах функции, но исходный код и вопрос касаются параметра шаблона, используемого для вызова функции.





Ваша функция 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
Ах, да. Забыл сказать, в чем заключалось исправление. consteval (или constexpr) делает вызов функции постоянным выражением. Проверка на переполнение делает код действительным, даже если функция constexpr (неопределенное поведение не допускается с помощью consteval)
На самом деле ваша проблема связана с конфликтом имен:
(не 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. Будет ли это?
Для меня это не так... Мы заявляем, что то, что хочет ОП, возможно, но не удалось воспроизвести точную проблему ОП.
Похоже, что моя система сборки использовала установленную в системе версию clang (14.0.0), а не ту, которую я установил через Homebrew. Компиляция вручную с помощью clang 18.1.4 (после обновления) работала нормально. Спасибо @Cem за тестирование различных версий компилятора и указание мне правильного направления!
Параметры функции не являются постоянными выражениями. Если бы они были, вы могли бы создать функцию, которая возвращает разные типы в зависимости от значения параметра. Это не то, что язык может поддерживать.