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?
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.
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.
@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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With