Reputation: 950
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?
Upvotes: 4
Views: 131
Reputation: 51835
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
decltype
yields 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
Upvotes: 4