Функция constexpr с задержкой инициализации локальных переменных
Я пытаюсь написать функцию constexpr
формы:
constexpr int foo(bool cond) {
int a, b, c;
if (cond) {
a = 1;
b = 2;
c = 3;
}
else {
a = -1;
b = -2;
c = -3;
}
return a + b + c;
}
Однако компилятор жалуется, что я использую неинициализированные переменные, несмотря на то, что возможная инициализация локальных переменных гарантирована.
Я мог бы перезаписать функцию для использования тройных операторов, т.е. int a = cond ? 1 : -1;
и т.д., но я бы предпочел не делать этого. Есть ли способ убедить компилятор, что локальные переменные будут инициализированы?
Ответы
Ответ 1
Однако компилятор жалуется, что я использую неинициализированные переменные, несмотря на то, что возможная инициализация локальных переменных гарантирована.
Инициализировать или инициализировать нет, нет "возможной инициализации". И для функций constexpr
существует требование, чтобы в [dcl.constexpr]:
Определение функции constexpr
должно удовлетворять следующим требованиям: [...] его тело функции должно быть = delete
, = default
или составной оператор, который не содержит [...] определение переменной нелитерального типа или статической или продолжительности хранения потоков или , для которой нет инициализация выполняется.
Невозможно иметь неинициализированные переменные в функциях constexpr
, что означает a
, b
и c
для вас.
Так что вы можете сделать? Вы можете просто инициализировать нуль a
, b
и c
. Это обостряет это требование. Или вы можете инициализировать a
, b
и c
внутри каждой области в if
. Или вы можете отложить до другой функции constexpr
для выполнения суммирования:
constexpr int f(int a, int b, int c) { return a+b+c; };
constexpr int foo(bool cond) {
if (cond) {
return f(1,2,3);
}
else {
return f(-1,-2,-3);
}
}
Существует много способов обойти это.
Ответ 2
Однако компилятор жалуется, что я использую неинициализированные переменные, несмотря на то, что возможная инициализация локальных переменных гарантирована.
Стандарт требует, чтобы все локальные переменные в функции constexpr
были инициализированы.
Из §7.1.5, пар. 3 ([dcl.constexpr]):
Определение функции constexpr
должно удовлетворять следующим требованиям: [...]
его тело функции должно быть = delete
, = default
или составной оператор, который не содержит [...]
определение переменной нелитерального типа или статической или длительности хранения потоков или для которой не выполняется инициализация. [...]
constexpr int uninit() {
int a; // error: variable is uninitialized
return a;
}
В С++ 17 вы можете использовать std::tuple
, структурированные привязки и IIFE (немедленное выражение функции), чтобы сохранить исходную структуру:
constexpr int foo(bool cond)
{
const auto [a, b, c] = [&cond]
{
if (cond)
{
return std::tuple(1, 2, 3);
}
else
{
return std::tuple(-1, -2, -3);
}
}();
return a + b + c;
}
Так как ваше условие и ветвление тривиально, то будет достаточно тернарного оператора. Фрагмент кода выше может помочь вам, если в будущем ваша логика инициализации станет более сложной, но одна из них должна быть достаточно хорошей:
constexpr int foo(bool cond)
{
const auto [a, b, c] = cond ? std::tuple(1, 2, 3)
: std::tuple(-1, -2, -3);
return a + b + c;
}
В С++ 14 вместо std::make_tuple
и std::get
можно использовать
constexpr int foo(bool cond)
{
const auto abc = cond ? std::make_tuple(1, 2, 3)
: std::make_tuple(-1, -2, -3);
return std::get<0>(abc) + std::get<1>(abc) + std::get<2>(abc);
}
В С++ 11 вы можете разделить функцию на две более мелкие:
template <typename TTuple>
constexpr int sum3(const TTuple& abc)
{
return std::get<0>(abc) + std::get<1>(abc) + std::get<2>(abc);
}
constexpr int foo(bool cond)
{
return cond ? sum3(std::make_tuple(1, 2, 3))
: sum3(std::make_tuple(-1, -2, -3));
}
Решение Barry, безусловно, лучше, если вы решите пойти по этому маршруту.
Все приведенные выше решения:
-
Сделайте переменные a
, b
, c
const
, что всегда хорошо.
-
Выполняйте только одну проверку на cond
, чтобы очень похож на структуру кода в OP.
Ответ 3
@Путь к Borgleader достаточно:
constexpr int foo(bool cond) {
int a=0, b=0, c=0;
if (cond) {
a = 1;
b = 2;
c = 3;
}
else {
a = -1;
b = -2;
c = -3;
}
return a + b + c;
}
компилируется без ошибок в С++ 11 и только предупреждения о том, что объявление переменной в функции constexpr является расширением С++ 14 и без предупреждения в режиме С++ 14 (с CLang 3.4.1)
Это чистое, простое чтение и запись и приближение к исходному коду. Но бесспорно, @Barry решение симпатичнее.