For some reason GCC regards variable a as an rvalue within the initialization of variable b (of which the expression is of type int&). And, as a result, rejects the code as it's not able to bind a non-const lvalue reference to an rvalue. Is this simply a compiler bug of GCC or does this expression somehow violates the C++20 standard?
int a{}, b = decltype((a)){a}; // gcc: can't bind non-const lvalue ref to rvalue
Demo
This is arguably a GCC bug, and arguably a known defect CWG 1521. I have submitted a report at GCC Bug 115085.
Firstly, let's be clear about what this code should be treated as:
int a{}, b = decltype((a)){a};
a is being value-initialized, and b (of type int) is being copy-initialized using postfix-expression consisting of a decltype((a)) simple-type-specifier and {a} within a braced-init-list.
a is an unqualified-id which is not move-eligible, and because a is a a variable, a is an lvalue.
Whether a appears within decltype or within braces is not relevant; it should be an lvalue in either case.
Therefore, decltype((a)) is int& (which all compilers seem to get right), but considering a to be an rvalue within braces is wrong.
However, b = decltype((a)){a} is defective as a whole.
[expr.type.conv] p2 states that the initializer should be a prvalue in this case, but this is impossible/nonsensical for reference types.
This issue is a long-standing defect CWG 1521.
Note that all compilers accept the following code:
int a{}, b = decltype((a))(a);
This makes sense because the use of direct-list-initialization shouldn't have an effect here, and both (a) and {a} should work the same.
Perhaps the bug is caused by GCC thinking that {a} is somehow aggregate-initialization, where each member is copy-initialized, and by some mechanism, prvalues emerge.
Another possible explanation is that we are doing copy-initialization of b, and a is being turned into an rvalue by lvalue-to-rvalue conversion/temporary materialization, and this happens too early, i.e. before the reference binding can happen.
All compilers also accept this code (as they should):
int a{}, &b{a};
This time, b is simply a reference which is direct-list-initialized using the lvalue a.
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