P1061 introduced packs in structured binding declarations, an excellent improvement that allows tuple-like types to be converted into packs while bundling remaining elements.
auto [x, ...rest, z] = std::tuple<int,int,int>{0,1,2};
auto t = std::forward_as_tuple(rest...);
However, pack expansions cannot directly initialize structured bindings:
auto [x, ...rest, z] = args...; // currently unsupported and always ill-formed
That's correct. The idea of a structured binding is that you have one thing on the left, one thing on the right, and you're giving names (binding names) to the pieces of the left-hand thing.
So for example we would expect this to work—
template<class... Ts>
void f(Ts... ts) {
auto obj = ts...;
}
int main() { f(42); }
—because `ts...` expands to `42`, and `auto obj = 42;` is OK. The only reason it doesn't work today is a quirk of the grammar. (There's also a special rule that says a pack-expansion must be sufficiently general:
https://eel.is/c++draft/temp#res.general-6.5 forbids templates containing pack-expansions that
must contain zero elements. But this doesn't hit that rule because
this pack-expansion must contain
one element. So I believe the issue here is that the `
declaration` grammar doesn't permit an ellipsis in that position.)
Compare this, which does work fine:
template<class... Ts>
void f(Ts... ts) {
auto obj = auto(ts...);
}
int main() { f(42); }
Here, the grammar doesn't get in our way, and `auto(42)` gives us the initializer for `int obj`. But again `auto [...xs] = auto(ts...)` doesn't work, because `int obj` is not structured-bindable. (It doesn't have any "elements"; it's just an int.)
We can also write:
template<class... Ts>
void f(Ts... ts) {
auto obj = {ts...};
}
int main() { f(42); }
But this doesn't work with `auto [...xs]`, because here we deduce `std::initializer_list<int> obj`, and `initializer_list<int>` is not structured-bindable. (Semantically it doesn't know how many elements it has; and mechanically, the data members it does have — a pair of pointers — are private, not public.)
Now, I agree that in a vacuum it would be nice if we could have
auto [...xs] = {ts...};
For example,
auto [x, y] = {42, "hello"};
This doesn't work today because (1) {42, "hello"} has no type, and can't deduce a consistent `T` to put in `initializer_list<T>` either; and (2) `initializer_list` is not structured-bindable. But it does seem relatively obvious that the programmer wants to give the value 42 to `x` and "hello" to `y`, as if by
auto x = 42;
auto y = "hello";
The problem is, that's not what a structured binding declaration does. What it's supposed to do is: Create a single object on the left-hand side, and bind names to its pieces. What is the type of that single object here? It can't be std::tuple; that's a library type. It could be an ad-hoc unnamed standard-layout struct type — I think that could work. I wouldn't pursue that myself, but if you really really wanted this kind of thing, that's the direction I might consider exploring.
my $.02,
Arthur