Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compiler wrongfully regards variable as rvalue

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

like image 697
303 Avatar asked May 29 '26 20:05

303


1 Answers

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.

Some more bug investigation

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.

like image 95
Jan Schultke Avatar answered May 31 '26 11:05

Jan Schultke



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!