A constexpr function with delayed initialization of local variables

我的未来我决定 提交于 2020-05-14 14:56:46

问题


I am trying to write a constexpr function of the form:

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

However, the compiler complains that I am using uninitialized variables, despite the fact that the eventual initialization of the local variables is guaranteed.

I could re-write the function to use ternary operators, that is, int a = cond ? 1 : -1;, etc., but I would prefer not to. Is there a way to convince the compiler that the local variables will be initialized?


回答1:


However, the compiler complains that I am using an uninitialized variables, despite the fact that the eventual initialization of the local variables is guaranteed.

Initialize, or initialize not, there is no "eventual initialization." And, for constexpr functions, there is a requirement that, in [dcl.constexpr]:

The definition of a constexpr function shall satisfy the following requirements: [...] its function-body shall be = delete, = default, or a compound-statement that does not contain [...] a definition of a variable of non-literal type or of static or thread storage duration or for which no initialization is performed.

Can't have uninitialized variables in constexpr functions, which is what a, b, and c are for you.

So what can you do? You could just zero-initialize a, b, and c. That gets around this requirement. Or you could initialize a, b, and c inside of each scope in the if. Or you could defer to another constexpr function to do the summing:

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

There are lots of ways around this.




回答2:


However, the compiler complains that I am using an uninitialized variables, despite the fact that the eventual initialization of the local variables is guaranteed.

The standard requires all local variables in a constexpr function to be initialized.

From §7.1.5, par. 3 ([dcl.constexpr]):

The definition of a constexpr function shall satisfy the following requirements: [...]

its function-body shall be = delete, = default, or a compound-statement that does not contain [...]

a definition of a variable of non-literal type or of static or thread storage duration or for which no initialization is performed. [...]

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

In C++17, you can use std::tuple, structured bindings, and IIFE (immediately-invoked function expression) to preserve your original structure:

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

Since your condition and branching is trivial, a ternary operator will suffice. The code snippet above may help you if in the future your initialization logic becomes more complex, but the one below should be good enough:

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

In C++14, you can use std::make_tuple and std::get instead:

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

In C++11 you can split the function in two smaller ones:

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 is definitely better if you decide to go down that route, though.


All the above solutions:

  • Make your a, b, c variables const, which is always a good thing.

  • Only perform a single check on cond, in order to closely resemble the structure of the code in the OP.




回答3:


@Borgleader way is enough:

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

compiles without error in C++11 and only warnings that variable declaration in a constexpr function is a C++14 extension and with no warning in C++14 mode (with CLang 3.4.1)

This is clean, simple to read and write and close to original code. But undoubtly, @Barry's solution is nicer.



来源:https://stackoverflow.com/questions/41509106/a-constexpr-function-with-delayed-initialization-of-local-variables

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!