Я хотел бы иметь возможность условно объявлять переменную 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
из-за использования функции, отличной от constexpr
std::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. В реальном случае тип моей переменной может быть любым (фундаментальные типы, а также классы).
Для целочисленных типов const
подойдет.
@Jarod42 Я так и думал, но GCC также принимает godbolt.org/z/jv5je67zo и reinterpret_cast
определенно не является constexpr.
const
может быть достаточно хорош для нецелых типов, компилятор, вероятно, оптимизирует его.
reinterpret_cast<double&>(some_double);
— это ноп, gcc может удалить его перед проверкой constexpr
. (если вместо этого вы используете float, gcc тоже отклоняет код).
Как я уже сказал, это упрощенный пример, мой фактический тип может быть классом (в моем случае это std::array
). Редактирую вопрос, чтобы было понятнее.
@Jarod42 О, понятно. Я все равно ожидаю, что GCC диагностирует существование reinterpret_cast, независимо от того, является ли это ошибкой, но, возможно, это нормально.
Использовать МАКРО?, #if __cplusplus <= 202302L #define CPP26_CONSTEXPR /*empty or const?*/ #else #define CPP26_CONSTEXPR constexpr #endif
.
Я не понимаю вопроса. Вы хотите сказать, что в вашем не упрощенном примере у вас та же проблема с gcc, что в нем есть что-то constexpr
, чего вы не хотите constexpr
? Потому что, кроме этого, ваш код уже делает то, что вы хотите, не так ли?
...или вы спрашиваете, как изменить main
так, чтобы другие компиляторы приняли код?
@463035818_is_not_an_ai Извините, замечание по gcc добавило шума в вопрос. Моя цель — создать код, который компилируется всеми компиляторами. Для более реалистичного варианта использования: stackoverflow.com/a/78676552/21691539: Я хотел бы объявить std::array
constexpr, если его инициализатор можно сделать постоянным выражением.
@ 11638718 совсем наоборот: ваш CE показывает, как заставить gcc выйти из строя, а не как заставить clang работать.
но разве это не тот пример, который вы хотели иметь? И теперь вопрос в том, как изменить constexpr auto w = create_val(4.0);
, чтобы сделать вывод, что create_val(4.0)
не является constexpre и, следовательно, автоматически delcare w
как не constexpr
? Если это не так, то я все еще в замешательстве
@Jarod42 с const
Я не могу повторно использовать свое значение в постоянно оцениваемом контексте. Например, если я объявлю v
как const
: constexpr auto z = create_val(static_cast<int>(v));
. (Тем не менее, я удивлен, что могу использовать его как NTTP). Означает ли это, что всегда лучше объявлять переменные const
вместо constexpr
?
@463035818_is_not_an_ai попробую перефразировать вопрос
@Эрстед, ОК, я понимаю. То есть вы, вероятно, имеете в виду godbolt.org/z/qrn4ETfnE?
Насколько я понимаю, вам нужен «constexpr_if_possible». Это то, что const
делает для целочисленных типов (унаследовано от C++98, когда constexpr
не существовало).
@Jarod42 Jarod42 Именно та формулировка, которая мне была нужна! а твой комментарий про const
можно перенести в любой тип?
@ 11638718 О, понятно, у меня сложилось впечатление, что мой комментарий во фрагменте сбивает с толку: это не ожидаемое поведение. Перефразирую еще раз...
Какое значение предоставляет constexpr_if_possible? Вы не можете использовать результат как постоянное выражение, например, параметр шаблона. Вы не гарантируете инициализацию во время компиляции. Кажется эквивалентным const?
Вам нужен «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. Не могу понять почему в стандарте. Я не знаю, ошибаются ли составители. Я думаю, что, возможно, эту часть ответа следует пересмотреть.
Более запутанный пример: const
целая переменная вроде бы принята в NTTP, но const
объекты классов нельзя использовать в качестве CNTTP: godbolt.org/z/1sxh3EKxv
В const int y = 42;
y
— это целочисленный тип (int
), поэтому const
эквивалентно constexpr
(поскольку 42
— постоянное выражение) (это не для const int x = argc;
, поскольку argc
не является постоянным выражением).
Думаю, это правильная ссылка en.cppreference.com/w/cpp/language/…. Особенно инициализаторы переменных ссылочного типа или целочисленного типа или перечислимого типа с указанием const, но только если инициализаторы являются постоянными выражениями. Таким образом, точный ответ будет таков: для целочисленного типа const
выполняет всю работу (удобство использования в константном контексте, если инициализатор сам по себе является константным выражением). В противном случае невозможно сделать переменную пригодной для использования в константном выражении. Вы согласны?
Точнее, const int x = initializer
эквивалентно constexpr int x = initializer
, если initializer
— постоянное выражение.
Да, мы согласны, но в другой формулировке.
«Как ни странно, gcc это принимает» gcc помечает некоторые математические функции как
constexpr
, даже если они не входят в стандарт (пока).