Подтвердить что ты не робот

Функция 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; и т.д., но я бы предпочел не делать этого. Есть ли способ убедить компилятор, что локальные переменные будут инициализированы?

4b9b3361

Ответ 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 решение симпатичнее.