Переменная, условно объявленная constexpr в соответствии с выражением ее инициализации

Я хотел бы иметь возможность условно объявлять переменную constexpr в соответствии со свойствами ее инициализатора.

Мой фактический вариант использования немного сложен, но его можно резюмировать следующим образом:

#include <cmath>
#include <type_traits>

template<typename T>
constexpr double create_val(T x)
{
    // some code that can be constant evaluated or not, according to T
}

int main() {

    constexpr_if_possible auto v = create_val(4);

    // w can only be constexpr under certain conditions
    // otherwise it should be at least const
    constexpr_if_possible auto w = create_val(4.0);

    return static_cast<int>(v*w);
}

где constexpr_if_possible на самом деле будет означать constexpr, когда выражение может быть вычислено константой, и const в противном случае.

Вот пример, иллюстрирующий это:

#include <cmath>
#include <type_traits>

template<typename T>
constexpr double create_val(T x)
{
    static_assert(std::is_arithmetic_v<T>);
    if constexpr (std::is_integral_v<T>)
    {
        // some leggit code in constant expression
        // only an example
        return static_cast<double>(x*x);
    }
    else
    {
        // some not usable code in constant expression
        // only an example
        return std::sqrt(x);
    }
}

int main() {

    // constexpr_if_possible is equivalent to constexpr here
    constexpr auto v = create_val(4);

    // conditions are not met, constexpr_if_possible is equivalent to const
    const auto w = create_val(4.0);

    return static_cast<int>(v*w);
}

clang и msvc по праву отклонили бы этот код с помощью constexpr w из-за использования функции, отличной от constexprstd::sqrt. (gcc принимает его и оптимизирует вычисления, вероятно, потому, что он уже объявляет std::sqrt как constexpr).
Прямой эфир

В соответствии с аргументом шаблона create_val, инициализированная переменная может быть или не быть constexpr.

Если бы я был в функции шаблона, я мог бы использовать if constexpr, но, возможно, ценой дублирования кода.

Очевидно, я также мог бы прочитать все коды, чтобы определить «вручную», возможна ли постоянная оценка, но, как показано в моем примере, когда я изменю языковой стандарт, мне придется снова проанализировать весь мой код, чтобы увидеть, изменилась ли ситуация. измененный.

Можно ли (и как) объявить v, w условно constexpr относительно их инициализатора? В псевдо-совершенно неправильном коде:

constexpr_if_possible auto w = create_val(...);

(спасибо @Jarod42 за помощь в формулировке constexpr_if_possible)

У меня сложилось впечатление, что синтаксис языка не позволяет этого, но есть ли какой-нибудь обходной путь?

NB. В реальном случае тип моей переменной может быть любым (фундаментальные типы, а также классы).

«Как ни странно, gcc это принимает» gcc помечает некоторые математические функции как constexpr, даже если они не входят в стандарт (пока).

Jarod42 03.07.2024 14:35

Для целочисленных типов const подойдет.

Jarod42 03.07.2024 14:37

@Jarod42 Я так и думал, но GCC также принимает godbolt.org/z/jv5je67zo и reinterpret_cast определенно не является constexpr.

cigien 03.07.2024 14:38
const может быть достаточно хорош для нецелых типов, компилятор, вероятно, оптимизирует его.
Jarod42 03.07.2024 14:41
reinterpret_cast<double&>(some_double); — это ноп, gcc может удалить его перед проверкой constexpr. (если вместо этого вы используете float, gcc тоже отклоняет код).
Jarod42 03.07.2024 14:43

Как я уже сказал, это упрощенный пример, мой фактический тип может быть классом (в моем случае это std::array). Редактирую вопрос, чтобы было понятнее.

Oersted 03.07.2024 14:43

@Jarod42 О, понятно. Я все равно ожидаю, что GCC диагностирует существование reinterpret_cast, независимо от того, является ли это ошибкой, но, возможно, это нормально.

cigien 03.07.2024 14:45

Использовать МАКРО?, #if __cplusplus <= 202302L #define CPP26_CONSTEXPR /*empty or const?*/ #else #define CPP26_CONSTEXPR constexpr #endif.

Jarod42 03.07.2024 14:47

Я не понимаю вопроса. Вы хотите сказать, что в вашем не упрощенном примере у вас та же проблема с gcc, что в нем есть что-то constexpr, чего вы не хотите constexpr? Потому что, кроме этого, ваш код уже делает то, что вы хотите, не так ли?

463035818_is_not_an_ai 03.07.2024 14:55

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

463035818_is_not_an_ai 03.07.2024 14:56

@463035818_is_not_an_ai Извините, замечание по gcc добавило шума в вопрос. Моя цель — создать код, который компилируется всеми компиляторами. Для более реалистичного варианта использования: stackoverflow.com/a/78676552/21691539: Я хотел бы объявить std::array constexpr, если его инициализатор можно сделать постоянным выражением.

Oersted 03.07.2024 15:02

@ 11638718 совсем наоборот: ваш CE показывает, как заставить gcc выйти из строя, а не как заставить clang работать.

Oersted 03.07.2024 15:13

но разве это не тот пример, который вы хотели иметь? И теперь вопрос в том, как изменить constexpr auto w = create_val(4.0);, чтобы сделать вывод, что create_val(4.0) не является constexpre и, следовательно, автоматически delcare w как не constexpr? Если это не так, то я все еще в замешательстве

463035818_is_not_an_ai 03.07.2024 15:15

@Jarod42 с const Я не могу повторно использовать свое значение в постоянно оцениваемом контексте. Например, если я объявлю v как const: constexpr auto z = create_val(static_cast<int>(v));. (Тем не менее, я удивлен, что могу использовать его как NTTP). Означает ли это, что всегда лучше объявлять переменные const вместо constexpr?

Oersted 03.07.2024 15:18

@463035818_is_not_an_ai попробую перефразировать вопрос

Oersted 03.07.2024 15:19

@Эрстед, ОК, я понимаю. То есть вы, вероятно, имеете в виду godbolt.org/z/qrn4ETfnE?

康桓瑋 03.07.2024 15:29

Насколько я понимаю, вам нужен «constexpr_if_possible». Это то, что const делает для целочисленных типов (унаследовано от C++98, когда constexpr не существовало).

Jarod42 03.07.2024 15:33

@Jarod42 Jarod42 Именно та формулировка, которая мне была нужна! а твой комментарий про const можно перенести в любой тип?

Oersted 03.07.2024 15:34

@ 11638718 О, понятно, у меня сложилось впечатление, что мой комментарий во фрагменте сбивает с толку: это не ожидаемое поведение. Перефразирую еще раз...

Oersted 03.07.2024 15:35

Какое значение предоставляет constexpr_if_possible? Вы не можете использовать результат как постоянное выражение, например, параметр шаблона. Вы не гарантируете инициализацию во время компиляции. Кажется эквивалентным const?

Jeff Garrett 03.07.2024 15:42
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
20
92
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вам нужен «constexpr_if_possible».

Это то, что const делает только для целочисленных типов (унаследовано от C++98, когда constexpr не существовало). то есть const int x = 42; эквивалентно constexpr int x = 42;, тогда как const int y = read_int_from(std::cin); не является constexpr.

const может быть достаточно для нецелых типов, компилятор, вероятно, оптимизирует его.
Для этих типов их нельзя использовать как NTTP, но если это необходимо, то вместо этого вам придется использовать constexpr.

C++23 снижает ограничения на функции constexpr, чтобы разрешить использовать функции, которые в будущем могут быть помечены как constexpr (стандартно), но нет ничего, что можно было бы объявить переменную как constexpr_if_possible.

Что касается вашего замечания по поводу NTTP, я теоретически с вами согласен, но практически CE нет:godbolt.org/z/sxshrq7d9. Не могу понять почему в стандарте. Я не знаю, ошибаются ли составители. Я думаю, что, возможно, эту часть ответа следует пересмотреть.

Oersted 03.07.2024 15:55

Более запутанный пример: const целая переменная вроде бы принята в NTTP, но const объекты классов нельзя использовать в качестве CNTTP: godbolt.org/z/1sxh3EKxv

Oersted 03.07.2024 16:02

В const int y = 42;y — это целочисленный тип (int), поэтому const эквивалентно constexpr (поскольку 42 — постоянное выражение) (это не для const int x = argc;, поскольку argc не является постоянным выражением).

Jarod42 03.07.2024 16:07

Думаю, это правильная ссылка en.cppreference.com/w/cpp/language/…. Особенно инициализаторы переменных ссылочного типа или целочисленного типа или перечислимого типа с указанием const, но только если инициализаторы являются постоянными выражениями. Таким образом, точный ответ будет таков: для целочисленного типа const выполняет всю работу (удобство использования в константном контексте, если инициализатор сам по себе является константным выражением). В противном случае невозможно сделать переменную пригодной для использования в константном выражении. Вы согласны?

Oersted 03.07.2024 16:13

Точнее, const int x = initializer эквивалентно constexpr int x = initializer, если initializer — постоянное выражение.

Oersted 03.07.2024 16:15

Да, мы согласны, но в другой формулировке.

Jarod42 03.07.2024 16:25

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