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
viis the name of an lvalue of typeTithat 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