Consider a C++20 program where in function foo
there is a structured binding auto [y]
. The function returns y
, which is converted in object of type A
. A
can be constructed either from const reference of from rvalue-reference.
#include <tuple> #include <iostream> struct A { A(const int &) { std::cout << "A(const int &) "; } A(int &&) { std::cout << "A(int &&) "; } }; A foo() { auto [y] = std::make_tuple(1); return y; } int main() { foo(); }
Which one of the constructors shall be selected according to C++20 language standard?
Clang selects A(const int &)
and GCC selects A(int &&)
, demo: https://gcc.godbolt.org/z/5q779vE6T
Does one of the compilers not support yet the standard in that respect?
A structured binding declaration performs the binding in one of three possible ways, depending on E. Case 1 : if E is an array type, then the names are bound to the array elements. Case 2 : if E is a non-union class type and tuple_size is a complete type, then the “tuple-like” binding protocol is used.
As usual, the binding will fail if e is a non-const lvalue reference: decltype(x), where x denotes a structured binding, names the referenced type of that structured binding. In the tuple-like case, this is the type returned by std::tuple_element, which may not be a reference even though a hidden reference is always introduced in this case.
Each structured binding has a referenced type, defined in the description below. This type is the type returned by decltype when applied to an unparenthesized structured binding. Each identifier in the identifier-list becomes the name of an lvalue that refers to the corresponding element of the array.
Structured binding is one of the newest features of C++17 that binds the specified names to subobjects or elements of initializer. In simple words, Structured Bindings give us the ability to declare multiple variables initialized from a tuple or struct.
I believe that Clang is correct.
TL;DR: some lvalues can be implicitly moved, but a structured binding is not such a 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 typeTi
that refers to the object bound tori
; the referenced type isri
.
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]) orco_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- [...]
[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.
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