11

To my knowledge, identifiers introduced by structured bindings in C++17 are in fact references to some "hidden" variable. Such that

auto [ a, b ] = std::make_tuple(1, 2);

is kind-of equivalent to

auto e = std::make_tuple(1, 2);
auto& a = std::get<0>(e);
auto& b = std::get<1>(e);

However, if I print out std::is_reference<decltype(a)>::value, I get 0 in the first case 1 in the second. Why is that?

Daniel Langr
  • 22,196
  • 3
  • 50
  • 93

2 Answers2

9

if I print out std::is_reference<decltype(a)>::value, I get 0 in the first case 1 in the second.

Why is that even if we can prove that a and b refer to the elements in the tuple and one can modify those values by means of them?
I'm not a language lawyer, but probably it is due to this bullet of the standard (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


Side note. You should use this form to do so that a and b refer to the elements in the tuple:

auto tup = std::make_tuple(1, 2);
auto & [ a, b ] = tup;

It follows a minimal, working example:

#include <tuple>
#include <type_traits>
#include <iostream>

int main() {
    auto tup = std::make_tuple(1, 2);
    auto & [ a, b ] = tup;
    a = 0;
    std::cout << a << ", " << std::get<0>(tup) << std::endl;
}

See it on Coliru. On the other side, what you get is a copy of the values using the expression below:

auto [ a, b ] = std::make_tuple(1, 2);

Here is an article that explains it better and is a bit more comprehensible than the standard for humans.

skypjack
  • 49,335
  • 19
  • 95
  • 187
  • @DanielLangr Ok, I tried to go a bit deeper, but probably a language lawyer is needed to validate or reject the answer. I would add the `language-lawyer` tag to the question if I was you. – skypjack Jun 21 '17 at 11:51
  • 1
    That's what I was looking for. Could you please delete all text except the last sentence and the quote below it? The rest is confusing or not related to my question. – Daniel Langr Jun 21 '17 at 12:33
  • @DanielLangr Uhm, it could be still interesting for future readers. Would it work for you if I move that part at the end of the question, so that relevant information are at the top? – skypjack Jun 21 '17 at 12:35
  • 1
    `you should use this form to get an actual reference` is plain wrong. – cpplearner Jun 21 '17 at 12:50
  • @cpplearner I tried to change a bit the wording. I guess the language is (one of) the problem(s), so feel free to suggest a different form. Thank you. – skypjack Jun 21 '17 at 12:56
  • `auto& [a,b] = make_tuple(1,2)` is ill-formed for the same reason that `auto& t = make_tuple(1,2)` is ill-formed. – Barry Jun 21 '17 at 13:20
  • @Barry In the example I didn't use that form. The reason is that it requires a `const` in that case, right? – skypjack Jun 21 '17 at 13:25
  • @skypjack Your answer literally states "You should use this form" referring to `auto&`. – Barry Jun 21 '17 at 13:28
  • @Barry Oh, I see. Fixed. Thank you. – skypjack Jun 21 '17 at 13:32
  • 1
    @DanielLangr You say you don't want to get references. Can you explain why? In a sense, the reference-ness of `a` and `b` is an implementation detail. `auto[a,b]=rhs;` make `a` and `b` references to a *copy* of the rhs, while `auto&[a,b]=rhs;` make `a` and `b` references to a *reference to* the rhs. – Yakk - Adam Nevraumont Jun 21 '17 at 18:05
  • @Yakk That was an answer to a comment, which was later deleted. I thought I didn't want to use the `auto&` form. – Daniel Langr Jun 23 '17 at 05:30
  • "_what you get is a copy of the values using the expression below: `auto [ a, b ] = std::make_tuple(1, 2);`_" This isn't true. That declares an unnamed tuple, and the names `a` and `b` bind directly to its members, not copies of its members (or members in a copy of it). Your suggestion for what people "_should use_" does the same thing, just in an unnecessarily verbose way (unless the OP actually needs the destructured object to have a name, i.e. if they need to pass it anywhere). – underscore_d Jan 12 '19 at 16:53
1

To my knowledge, identifiers introduced by structured bindings in C++17 are in fact references to some "hidden" variable.

If by "reference" you mean the language construct reference, this isn't quite correct. The specifiers in the declaration appertain to the "hidden variable" you speak of. The reference qualifier is optional. The code you presented would be more like this:

const auto& e = std::make_tuple(1, 2);
using E = remove_reference_t<decltype((e))>;
std::tuple_element<0, E>::type& a = get<0>(e);
std::tuple_element<1, E>::type& b = get<1>(e);
  • 1
    This code doesn't allow to assign `a` a different value. My code does (both options); see [online-IDE example](https://wandbox.org/permlink/or2YRMGYJl97LiNL). – Daniel Langr Jun 21 '17 at 10:01
  • 1
    The code is not actual code, but a translation of the structured binding does. – user8193326 Jun 21 '17 at 10:03