constexpr函数,具有局部变量的延迟初始化

时间:2017-01-06 15:44:09

标签: c++ 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;等,但我不愿意。有没有办法说服编译器初始化局部变量?

3 个答案:

答案 0 :(得分:31)

  

然而,编译器抱怨我使用的是未初始化的变量,尽管事实上保证了局部变量的最终初始化。

初始化或初始化,没有"最终初始化。"而且,对于constexpr函数,需要在[dcl.constexpr]:

  

constexpr函数的定义应满足以下要求:[...] function-body 应为= delete= default复合语句,它不包含非文字类型或静态或线程存储持续时间的变量的定义,或者,其中没有   初始化已执行

constexpr个函数中没有未初始化的变量,abc适合您。

那你能做什么?您可以对abc进行零初始化。这绕过了这个要求。或者,您可以在a中的每个范围内初始化bcif。或者您可以按照另一个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);
    }    
}

有很多方法可以解决这个问题。

答案 1 :(得分:13)

  

然而,编译器抱怨我使用的是未初始化的变量,尽管事实上保证了局部变量的最终初始化。

该标准要求初始化constexpr函数中的所有局部变量。

  

来自§7.1.5, par. 3 [dcl.constexpr]

     
    

constexpr函数的定义应满足以下要求: [...]

         

其函数体应为= delete= default或不包含 [...]的复合语句

         

非文字类型或静态或线程存储持续时间的变量的定义,或者不执行初始化的定义。 [...]

constexpr int uninit() {
    int a;     // error: variable is uninitialized
    return a;
}
  

在C ++ 17中,您可以使用std::tuplestructured bindingsIIFE (immediately-invoked function expression)来保留原始结构:

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;
}

在C ++ 14中,您可以使用std::make_tuplestd::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);
}

在C ++ 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's solution肯定会更好。

以上所有解决方案:

  • 制作abc变量const,这总是一件好事。

  • 仅对cond执行一次检查,以便与OP中的代码结构非常相似。

答案 2 :(得分:1)

@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;
}

在C ++ 11中编译时没有错误,并且只警告constexpr函数中的变量声明是C ++ 14扩展并且在C ++ 14模式下没有警告(使用CLang 3.4。 1)

这是干净的,易于阅读和编写,并且接近原始代码。但毫无疑问,@Barry's solution更好。