I believe that Clang is correct.
TL;DR: some lvalues can be implicitly moved, but a structured binding is not such a lvalue.
- The name of a structured binding is an lvalue:
[dcl.struct.bind]/1:
A structured binding declaration introduces the identifiers v0
, v1
, v2
,… of the identifier-list as names of structured bindings.
[dcl.struct.bind]/4:
Each vi
is the name of an lvalue of type Ti
that refers to the object bound to ri
; the referenced type is ri
.
- An variable name (which is normally an lvalue) can be moved in a
return
statement if it names an implicitly movable entity:
An implicitly movable entity is a variable of automatic storage duration that is either a non-volatile object or an rvalue reference to a non-volatile object type.
In the following copy-initialization contexts, a move operation is first considered before attempting a copy operation:
- If the expression in a
return
([stmt.return]) or co_return
([stmt.return.coroutine]) statement is a (possibly parenthesized) id-expression that names an implicitly movable entity declared in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression, or
- [...]
- As can be seen in the definition of implicitly movable entity, only objects and (rvalue) references can be implicitly moved. But a structured binding is neither.
[basic.pre]/3:
An entity is a value, object, reference, [or] structured binding[...].
So I believe that a structured binding cannot be implicitly moved.
If y
were an object or reference, then it would be implicitly movable in return y;
.
Edit: C++17 as written specified that structured bindings to tuple members are references. This was corrected by CWG 2313.
int y = 1; return y;
, then clang usesint&&
constructor. Is id-expression denoting a variable not an lvalue (or are both compilers wrong in that case, or is there some rule that applies to that case but does not apply to structured bindings, or something else)? – Mebaney
is anint
, so it qualifies for rvalue conversion on return. In the op's exampley
is actually a reference to the sub-object of some unnamed object the structured binding declared. That stops the move if it is an lvalue reference. I still can't figure out which one is correct though :( – Goldmanreturn
statement, in some cases it is treated as rvalue (first). The rule is well-known. – Mixieauto&
is not used. – Mebane[]
are actually references into the object that the structured binding "captured". The reference qualification on structured binding applies to that captured object, not the bindings. – GoldmanA a = y;
then both compilers correctly use the lvalue reference constructor, which strengthens my guess that it's that particular rule that GCC (wrongly) applies. – Mebane