6

I am trying to update a variable passed to a function with a structured binding:

#include <iostream>
#include <tuple>
#include <utility>

std::pair<int, double> func(double y)
{
    return {1, 1.3+y};
}

int main() {
    double y = 1.0;
    auto [x, y2] = func(y);
    y = y2;
    std::cout << "x = " << x << ", y = " << y << '\n';
    return 0;
}

Is it possible to avoid the extra assignment y = y2 and using something like

[auto x, y] = func(y); // this does not work

or

std::tie(auto x, y) = func(y); // this does not work
Håkon Hægland
  • 39,012
  • 21
  • 81
  • 174
  • 3
    I will ask the obvious just in case: you can't simply move the declaration of `y` to the structured binding? – dfrib Nov 16 '20 at 15:46
  • 1
    I think the idea is that `y` should be already declared because it's being used as an argument to `func`. In this code snippet, it could be replaced with `auto [x, y] = func(1.0)`, yes. – Nathan Pierson Nov 16 '20 at 15:52
  • 1
    It looks like people tried to come up with something hybridizing structured bindings and `std::tie` [here](https://stackoverflow.com/questions/42590449/c17-structured-binding-that-also-includes-an-existing-variable). Not sure if either of those solutions will be very useful for you. – Nathan Pierson Nov 16 '20 at 15:56
  • In `auto [x, y] = foo()`, `auto` doesn't apply to each individual identifier. Instead it applies to a single invisible variable that's initialized with the return value of `foo()`. Identifiers declared in brackets become (loosely speaking) magical aliases for the members of this hidden variable. https://en.cppreference.com/w/cpp/language/structured_binding – HolyBlackCat Nov 16 '20 at 16:45

3 Answers3

2

A third option:

decltype(func(y).first) x;
std::tie(x, y) = func(y);
Robert Andrzejuk
  • 5,076
  • 2
  • 22
  • 31
2

A structured binding is a declaration; it cannot be used e.g. for assignment into an already declared variable.

If you are allowed to move the declaration of y and you ever only need it to make a function call, you could abuse the scope of the the capture list of an immediately invoked lambda, and let it shadow (only within the scope of the lambda) the variable y that is declared as part of a structured binding, which is in turn initialized using the return from the immediately invoked lambda:

auto [x, y] = [y = 1.0](){ return func(y); }();
            // ^^^^^^^ actually not at all in namespace scope,
            //         but a data member of the closure type
            //         of the lambda expression.

You could likewise use a named lambda:

const auto l = [y = 1.0](){ return func(y); };
auto [x, y] = l();

As is typically the case with shadowing alongside the somewhat complex scoping rules of C++, this is likely only to confuse readers, though.

dfrib
  • 70,367
  • 12
  • 127
  • 192
1

To answer your question narrowly:

No, you can't reuse an existing variable in a structured binding. Structured binding always declares new variables.

That being said, there are other options - like std::tie, as @Robert A has demonstrated.

Marshall Clow
  • 15,972
  • 2
  • 29
  • 45