struct { int a; int b; } x { 1, 2 };
auto [b, a] = x;
Now b is 1 and a is 2, even though in the original structure b was 2 and a was 1. This is contrary to how most other languages with this feature behave.(I wrote a bit about this once: https://thebreakfastpost.com/2017/11/15/c17-destructuring-bi...)
I haven't had the opportunity to write any C++17 in production yet, so I'm not sure how dangerous this dangerous-looking thing actually is.
I realise that C++11 structure initialisation syntax already blurs this distinction. That's already a bit dangerous in my view, although in both cases I can understand why they chose to do it that way.
Are there other languages in which struct (not tuple) bindings happen by declaration order?
then... just use the struct directly if that's what you want ? But a single binding can map to multiple structs, especially in generic code :
template<typename T>
double dist(T point) {
auto& [x, y] = point;
return std::sqrt(x * x + y * y);
}
should work with T's that look like struct Point { float f[2]; }
or struct Point { float x, y; }
or struct Point { float p1, p2; }
or std::tuple<float, float>
or ...The b and a in "auto [b,a]" are arbitrary names you are applying to the result of the destructuring assignment, and they are essentially unrelated to the names of the struct members.
If you write "auto [apples, oranges] = x;" it means exactly the same thing as [b,a] and [a,b]: two ints, each with an arbitrary identifier, the first with value x.a and the second with value x.b.
The only unusual and slightly flaky thing that's going on is interpreting a struct as a sequence; the order of struct fields is usually relevant for its memory layout, but not for language features that use identifiers.
What you appear to want is something entirely different: constructing something that automatically mirrors the names of the struct members.
For example Java reflection allows enumerating the fields in a class, getting their names and other metadata, and extracting from an object the value of a field of its class. You could then populate a map with a destructured copy of an object in which each value has the same name as in the original object.
I do get what you're saying; I can see why it's done this way. C++ lacks tuples at the language level, so there is no other way to do an anonymous tuple-style binding. Being forced to bind to the same name as the structure element would be inconvenient for various reasons. It makes sense. I just think it's problematic that changing the declaration order of a struct can silently break code that uses it. And it is different from other languages - not just natty ones like ML but also mainstream ones like Javascript ES6 - which makes it a point of some curiosity.
> What you appear to want is something entirely different: constructing something that automatically mirrors the names of the struct members.
It's all just a question of declaring and assigning local variables, there would be no mysterious objects or runtime reflection involved in either case.
Struct X could have a constructor
public X(int first,int second):a{first},b{second}
but a caller intending to set a=1 and b=2 could call X(2,1) instead (particularly if last time they checked the constructor was public X(int first,int second):b{first},a{second}).
You certainly don't want to prevent this "mistake" by enforcing that the first parameter must be a local variable called a and the second must be a local variable called b and that the constructor parameters must be called a and b too.This sort of easily detected error is exactly the same as writing [b,a] instead of [a,b] in a deconstructing assignment.
Treating a sequence of fields in a class as a tuple is "unusual and slightly flaky" because few and unimportant languages do it, because it's very new for C++, and because it faces complications like inheritance, not because it is wrong or bad; someone might be slow to adapt, but everyone else will benefit.
In practice, only very plain and well documented structs will be suitable for deconstructing assignment; for example, in code generated from data serialization schemas there is a natural and well known order for class members.
An Objective-C programmer might disagree with the last part. But of course it's possible to come up with examples of other things already in C++ that are equally error-prone. That doesn't mean this new thing won't be error-prone too.
I bet that many C++ programmers who are passingly familiar with C++17 would agree that the second line here:
struct { int a; int b; } x { 1, 2 };
auto [a, b] = x;
is syntactic sugar for auto a = x.a;
auto b = x.b;
Introductory material about structured bindings routinely uses examples like this, where the names happen to match those in the structure, without making it clear that this is not what actually goes on.> This sort of easily detected error
"Easily detected" in C++ means the compiler detects it. The rest might as well be Javascript.
> Treating a sequence of fields in a class as a tuple is "unusual and slightly flaky" because few and unimportant languages do it
Can you name one? This is one thing I've been trying to discover here, without success.
> In practice, only very plain and well documented structs will be suitable for deconstructing assignment
I do agree with this -- good practice should be to use it only for "obvious" cases.
I will use this feature, when I get the opportunity. I use element-wise structure initialisation often enough. It's convenient, this will be convenient. It also bites me occasionally and so will this.
This is terrible for safety. Which is not surprising in a long line of questionable language design choices made for C++.