23

Yesterday I've seen an interesting question here on SO about structured binding.
We can sum up it as it follows. Consider the example code below:

#include <tuple>
#include <type_traits>

int main() {
    auto tup = std::make_tuple(1, 2);
    auto & [ a, b ] = tup;
    // the following line won't compile for a isn't a reference
    // static_assert(std::is_reference_v<decltype(a)>);
}

In this case decltype(a) is int (probably) because of this bullet (working draft):

if e is an unparenthesized id-expression naming a structured binding [...], decltype(e) is the referenced type as given in the specification of the structured binding declaration

Here is a snippet on wandbox provided by @Curious in the comments for those that are interested. It shows that actually a isn't a reference, nothing more.
So far so good for the original question, OP asked why it was int instead of int & and the standard says that looked like an acceptable answer.

Anyway, I'd like to know why the committee decided so. At the end of the day, a refers to an element in the tuple and I can modify that element through a. In other terms, the declaration of a looks like the one of a reference, it behaves similarly to a reference but it's not a reference.

I can live with this, but I'd like to know what are the reasons behind that. Why decltype(a) cannot be simply int &? Is there a meaningful reason that a profane can understand?

skypjack
  • 49,335
  • 19
  • 95
  • 187
  • Can you extend your example to actually demonstrate that `a` is not a reference? It should be fairly easy to `static_assert` something like `std::is_reference::value`. – Toby Speight Jun 22 '17 at 09:53
  • @TobySpeight In the original question there is the example you are asking for. That being said, you can simply print out `std::is_reference_v` and it will return 0, as well as `std:.is_same_v`, while `std:.is_same_v`will return 1. That's quite simple. – skypjack Jun 22 '17 at 09:55
  • 1
    @TobySpeight use an online compiler https://wandbox.org/permlink/BKLeZRzPubEsPC5l? – Curious Jun 22 '17 at 10:00
  • 3
    I absolutely hate how `a` and `b` are not references here – Curious Jun 22 '17 at 10:04
  • @Curious I agree and that's why I'm interested in the actual reasons, if any. – skypjack Jun 22 '17 at 10:05
  • 2
    In [section 11.5.3](http://eel.is/c++draft/dcl.struct.bind#3), there's text saying "*Given the type `Ti` designated by `std​::​tuple_­element​::​type`, each `vi` is a variable of type “reference to `Ti`” initialized with the initializer, where the reference is an *lvalue reference* if the initializer is an *lvalue* and an *rvalue* reference otherwise; the referenced type is `Ti`.*" That seems to me to suggest that `auto & [ ]` ought to declare references... – Toby Speight Jun 22 '17 at 10:16
  • @TobySpeight Unfortunately it doesn't. I see your point, but it doesn't conform with the bullet that rules on `decltype`. That's why it's confusing me actually. – skypjack Jun 22 '17 at 10:20
  • @Curious I hate how `decltype(a)` has a special case that makes `int a; int &b = a;` distinguishable from `int b; int &a = b;` - it breaks a nice symmetry (and I see this flaw as leading to the problem in this question) – M.M Jun 22 '17 at 10:29
  • In other words , `a` is an lvalue that designates an `int`; the question is what information do you hope to obtain by `decltype(a)` ? How will you use the result? – M.M Jun 22 '17 at 10:31
  • @M.M And what would you expect [this result](https://wandbox.org/permlink/7gGd8O80F3hWx0Lx) to be when you're told `decltype(a)` is not a reference? – O'Neil Jun 28 '17 at 15:18
  • @O'Neil well that's what this question is about – M.M Jun 28 '17 at 21:13

2 Answers2

11

I wrote this yesterday:

decltype(x), where x is a structured binding, names the referenced type of that structured binding. In the tuple-like case, this is the type returned by std::tuple_element, which may not be a reference even though the structured binding itself is in fact always a reference in this case. This effectively emulates the behavior of binding to a struct whose non-static data members have the types returned by tuple_element, with the referenceness of the binding itself being a mere implementation detail.

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • Damnit, I've read that page yesterday, probably before the edit. – skypjack Jun 22 '17 at 10:31
  • 3
    So, to put it in simple words, it's like `some_struct.member`? – Daniel Jour Jun 22 '17 at 11:08
  • 1
    So, `auto[a,b] = exp` and `auto&[a,b] = exp` have very different behavior (for many cases of exp), yet `decltype(a)` in both cases is identical? – Yakk - Adam Nevraumont Jun 22 '17 at 22:16
  • @Yakk seems like it, DanielJour and T.C.'s analogy explains this pretty nicely, I think I understand this now – Curious Jun 23 '17 at 15:00
  • @T.C. if you wrote that could you maybe try and move that explanation somewhere near the top of the page? Because this makes what structured bindings actually do a lot clearer (to me at least, I suspect to others as well) and a lot of what is on that page there is hard to understand (for mortals like me) – Curious Jun 23 '17 at 15:13
  • So `decltype` returns a referenced type meaning if it returns `int`, it actually implied `int&`? – xyf Feb 17 '23 at 02:44
-1

This topic has been covered before (look in the structured-bindings tag) and the behavior you're talking about is even addressed in the second answer. However, the rationale is spelled out in p0144r2 section 3.5:

Should the syntax be extended to allow const/&-qualifying individual names' types?

For example:

auto [&x, const y, const& z] = f(); // NOT proposed

We think the answer should be no. This is a simple feature to store a value and bind names to its components, not to declare multiple variables. Allowing such qualification would be feature creep, extending the feature to be something different, namely a way to declare multiple variables.

If we do want to declare multiple variables, we already have a way to spell it:

 auto val    = f();
 T& x        = get<0>(val);
 T2 const y  = get<1>(val);
 T3 const& z = get<2>(val);
user141770
  • 119
  • 1
  • 3
    I don't think this is relevant: the question is why the reference qualifier on the structured binding as a whole does not apply to both declared elements thereof - not why those elements can't be individually qualified. – underscore_d Jun 23 '17 at 15:03