Reputation: 21560
I had always avoided initializations like the following
const auto& x = a, y = b;
const int* const x = ptr_1, *const y = ptr_2; // wot
For the reason that the reference and pointer qualifiers don't apply to both the initializations. Granted it's one of the first things beginners learn, the ambiguity associated with it makes me feel like the following is clearer and requires less thought on the reader's end
const auto& x = a;
const auto& y = b;
With C++17 and structured bindings I was happy and saw lots of potential. C++17 outlawed what C++14 and C++11 had failed to fix, auto x {1}
is an int
and not std::initializer_list<int>
. But why does the following code not work?
const auto& [x, y] {a, b};
const auto& [x, y] = {a, b};
The latter is in line with the new rules for auto deduction and initializer lists, the expression on the right hand side is treated as an initializer list. But for the former compilation fails with the following error
initializer for variable '[a, b]' with type 'const auto &' contains multiple expressions
Is there any way I can declare both x and y with the structured bindings syntax without having to resort to tuples, pairs and the like? Also why is the former in the code example above ill formed code? Is there an ambiguity in that syntax?
Upvotes: 4
Views: 2946
Reputation: 137404
Structured binding is, so to speak, for "unpacking" things. It's not designed to be a way to combine normal declarations. That const auto&
applies to neither a
nor b
, despite the appearance.
Your particular attempt violates [dcl.dcl]/8:
A simple-declaration with an identifier-list is called a structured binding declaration ([dcl.struct.bind]). [...] The initializer shall be of the form “
= assignment-expression
”, of the form “{ assignment-expression }
”, or of the form “( assignment-expression )
”, where the assignment-expression is of array or non-union class type.
int a = 1, b = 2;
const auto bitand <:x, y:> = std::tie(a, b);
This structured binding declaration is (very) roughly equivalent to
const auto bitand __e = std::tie(a, b); // hidden variable
auto and x = std::get<0>(__e);
auto and y = std::get<1>(__e);
(The real thing uses tuple_element
, not auto
.)
Notes:
const auto bitand
applies to the hidden variable and only the hidden variable. x
and y
are always references even if you write just auto
; whether their referent is const
depends on the const
propagation properties of the initializer's type.x
and y
are of type "reference to int"; it is valid to write x = 1;
.decltype
wording.These semantics are unsurprising if we are talking about unpacking a struct, etc., with two "reference to int" members; a const
on such things doesn't actually affect the referent's constness. OTOH, you are in for a bad surprise if you want to use structured binding declarations for something they aren't designed to do.
Upvotes: 4
Reputation: 10939
This syntax is just not supported. You can only unpack aggregate classes and objects which for which std::get
has been overloaded: https://skebanga.github.io/structured-bindings/
Unfortunately, you cannot really make use of the cool deduction guide because you want a reference to a
and not to the tuple member. Thus you have to write out the template parameter list.
#include <tuple>
int main()
{
int a = 1;
int b = 2;
const auto& [x, y] = std::tuple<int&,int&>{a, b};
}
You could also not be as stupid as me and read the docs correctly.
#include <tuple>
int main()
{
int a = 1;
int b = 2;
const auto& [x, y] = std::forward_as_tuple(a, b);
}
const auto& [x, y] = std::tie(a, b);
works as well.
Upvotes: 1