Hacker News new | past | comments | ask | show | jobs | submit login

Mine, tentatively, is that the binding is made by declaration order, not by name:

   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.




This behaviour is the only reasonable one; "auto [b, a] = x;" cannot be considered wrong in any theoretical or practical way.

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.


Your reply is written as if you think my objection is absurd - then in the middle of it you concede exactly the thing I'm objecting to, and describe it as "unusual and slightly flaky"!

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.


No, it doesn't make sense. Order coupling between destructuring assignment targets and class fields is very similar to order coupling between formal and actual parameters of functions, which is easier to get wrong and for which no protection exists.

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.


> a caller intending to set a=1 and b=2 could call X(2,1) instead [...] 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.

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.


> changing the declaration order of a struct can silently break code that uses it

This is terrible for safety. Which is not surprising in a long line of questionable language design choices made for C++.


Is it, though? x.a and x.b, and a and b, name different things. Why should they be correlated? Apart from ML, I which pattern matching works differently, I don't know of any language where destructuring tuples tries to match names rather than position (even in ML, if you pattern match tuples rather than records).


Because this is a struct, not a tuple. Historically at least, I expect C/C++ structure elements to be identified by name rather than by declaration order. Changing the declaration order ideally would not silently change the meaning of code that uses the struct.

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?


> Historically at least, I expect structure elements to be identified by name rather than 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 ...




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: