Consider the following example (available here on Compiler Explorer), matching the 3 scenarios of structured-binding:
{ // array
auto value = std::array{ 1, 2 };
auto & [ v1, v2 ] = value;
static_assert(std::same_as<int, decltype(v1)>);
}
{ // tuple
auto value = std::tuple{ 1, 2 };
auto && [ v1, v2 ] = std::move(value);
static_assert(std::same_as<int, decltype(v1)>);
}
{ // user-defined type
struct toto{ int i; char c; };
const auto value = toto{ 1, 2 };
auto && [ v1, v2 ] = value;
static_assert(std::same_as<const int, decltype(v1)>);
}
I have troubles understanding why cv-qualifiers are propagated to the identifiers, but not the ref-qualifiers.
In the example above, I feel like it is kind of counter-intuitive that v1 is a possibly const-qualified int, but never ref-qualified.
What am I missing?
Your three decltype(v1) specifiers all yield the referenced type of the v1 expression, which is int (with the const qualifier, in the third case). From cppreference:
If the argument is an unparenthesized id-expression naming a structured binding, then
decltypeyields the referenced type (described in the specification of the structured binding declaration).
However, if we add an extra set of parentheses around v1, we convert that to an 'ordinary' lvalue expression, which includes the reference qualifier. From the same cppreference page:
Note that if the name of an object is parenthesized, it is treated as an ordinary lvalue expression, thus
decltype(x)anddecltype((x))are often different types.
Thus, just taking your first (array) case, the following assertion fails:
auto value = std::array{ 1, 2 };
auto& [v1, v2] = value;
static_assert(std::same_as<int, decltype((v1))>); // Fails: int != int&
But the assertion succeeds (again) if we add the reference qualifier to both sides of the test:
auto value = std::array{ 1, 2 };
auto& [v1, v2] = value;
static_assert(std::same_as<int&, decltype((v1))>); // OK: int& == int&
Note that, because the parenthesized version of v1 is an lvalue expression, in the second and third examples you give, decltype((v1)) will yield int& rather than int&&:
auto value = std::tuple{ 1, 2 };
auto&& [v1, v2] = std::move(value);
static_assert(std::same_as<int&&, decltype((v1))>); // Fails
static_assert(std::same_as<int&, decltype((v1))>); // OK
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