3

If you utilize structured bindings like so

auto [a, b, c] = std::make_tuple(1, 10.0, "string object"s);

then will the copies from the returned tuple be elided and the objects go straight into a, b and c or will the initializations be move constructions from the individual tuple elements? I doubt that this would cause a copy to happen but I am not sure about whether the description of mandatory copy elision in the standard handles this case.

Curious
  • 20,870
  • 8
  • 61
  • 146
  • "will the copies from the returned tuple be elided and the objects go straight into a, b and c" No. "will the initializations be move constructions from the individual tuple elements" No. – cpplearner Jan 21 '17 at 22:01
  • @cpplearner then are they copied? – Curious Jan 21 '17 at 22:06

1 Answers1

4

As covered by this excellent answer, the declaration is equivalent to:

auto e = std::make_tuple(1, 10.0, "string object"s);
int& a = get<0>(e);
double& b = get<1>(e);
std::string& c = get<2>(e);

except that there isn't a name e. The get function, in this context, yields an lvalue reference to the selected item.

In C++17, auto name = prvalue; is defined to declare an object called name of type decltype(prvalue) initialized with the prvalue expression -- there's no intermediate temporary that was elidable as in previous versions.

In other words, the e declaration behaves exactly the same as:

std::tuple<int, double, std::string> e {1, 10.0, "string object"s};

and then a,b,c are references to the elements of that tuple.


Note: The above explanation corresponds to the latest C++17 draft sources; behaviour may change before C++17 is finalized of course.

Community
  • 1
  • 1
M.M
  • 138,810
  • 21
  • 208
  • 365
  • In this example, `a`,`b`,`c` are `&&`s and `get` is called on the equivalent of `std::move(e)`. – T.C. Jan 22 '17 at 01:22
  • @T.C. `e` is an lvalue, so `get(e)` should be the version that takes lvalue reference and returns lvalue reference ? (But even if you're right, the end result would be the same since a,b,c are also lvalues). [dcl.decomp/3] currently says "Otherwise, the initializer is `get(e)`" – M.M Jan 22 '17 at 01:27
  • 1
    The wording here is pretty tricky. The key sentence is "In either case, `e` is an lvalue if the type of the entity `e` is an lvalue reference and an xvalue otherwise." – T.C. Jan 22 '17 at 01:28
  • @T.C. OK, so the `e` in [dcl.decomp]/3 is not the same `e` as in /1 , which was specified to behave as-if by `auto e = prvalue;` (which would be an lvalue in all cases) – M.M Jan 22 '17 at 01:31
  • Right, it's not quite `e`-the-variable introduced in /1. Essentially this is core-language-style perfect forwarding. – T.C. Jan 22 '17 at 01:34
  • In the lazy-evaluation proposal, having use of `c` invoke `get<2>(move(e))` at point of use, would mean that `string s = c;` would actually move out of `e` which seems unintuitive – M.M Jan 22 '17 at 01:34
  • Yes, they'll definitely have to tweak this part to make `c` remain an lvalue somehow. – T.C. Jan 22 '17 at 01:37
  • @T.C. OK. I might leave my answer as-is then - even if it's technically xvalue version of `get`, the as-if result is the same; a,b,c should be lvalues referring to `e` – M.M Jan 22 '17 at 02:01