6

Is there any way to shadow existing variables when destructuring a std::pair? For example if I have the following functions defined:

#include <iostream>
#include <utility>

std::pair<int, int> returns_pair(int a, int b)
{
    return std::make_pair(a * a, b * b);
}

void prints_two_variables(int a, int b)
{
    std::cout << a << "\n" << b << "\n";
}

Then this main function works fine, since I make new variables from the returned std::pair:

int main()
{
    int a = 2;
    int b = 3;
    auto [x, y] = returns_pair(a, b);
    prints_two_variables(x, y);
    return 0;
}

Output:

4
9

But I can't use the same variable names and shadow the existing variables, since this tries to actually declare them again:

int main()
{
    int a = 2;
    int b = 3;
    auto [a, b] = returns_pair(a, b);
    prints_two_variables(a, b);
    return 0;
}

Error:

main.cpp: In function ‘int main()’:
main.cpp:12:15: error: conflicting declaration ‘auto a’
    auto [a, b] = returns_pair(a, b);
              ^
main.cpp:10:9: note: previous declaration as ‘int a’
    int a = 2;
        ^
main.cpp:12:15: error: conflicting declaration ‘auto b’
    auto [a, b] = returns_pair(a, b);
              ^
main.cpp:11:9: note: previous declaration as ‘int b’
    int b = 3;
        ^

I tried also without the auto, but that gave a totally different error:

int main()
{
    int a = 2;
    int b = 3;
    [a, b] = returns_pair(a, b);
    prints_two_variables(a, b);
    return 0;
}

Error:

main.cpp: In lambda function:
main.cpp:12:12: error: expected ‘{’ before ‘=’ token
    [a, b] = returns_pair(a, b);
           ^
main.cpp: In function ‘int main()’:
main.cpp:12:31: error: no match for ‘operator=’ (operand types are ‘main()::’ and ‘std::pair’)
    [a, b] = returns_pair(a, b);
                              ^
main.cpp:12:10: note: candidate: main()::& main()::::operator=(const main()::&) 
    [a, b] = returns_pair(a, b);
         ^
main.cpp:12:10: note:   no known conversion for argument 1 from ‘std::pair’ to ‘const main()::&’

Is there any way to accomplish this?

ruohola
  • 21,987
  • 6
  • 62
  • 97
  • 4
    Would [`std::tie`](https://en.cppreference.com/w/cpp/utility/tuple/tie) be useful here? – Eljay Dec 03 '19 at 15:27

2 Answers2

13

Is there any way to destructure a pair, while shadowing existing variables?

No. A structured binding declaration always introduces identifiers, it cannot shadow or assign over existing variables. This is ill-formed:

int i = 4;
auto [i] = std::tuple(5);

for the same reason this is ill-formed:

int i = 4;
int i = 5;

If what you want to do is overwrite, you can use tie and assign:

std::tie(a, b) = returns_pair(a, b);

This works in this scenario, but not in the general case where returns_pair might return a struct with two public members.

Barry
  • 286,269
  • 29
  • 621
  • 977
9

What you want is std::tie. That will create a a std::tuple of references to the parameters and allow you to reassign the pair to the elements it was created from. That would look like

int main()
{
    int a = 2;
    int b = 3;
    std::tie(a, b) = returns_pair(a, b);
    prints_two_variables(a, b);
    return 0;
}

Remember to also #include the <tuple> header so you can use std::tie.

ruohola
  • 21,987
  • 6
  • 62
  • 97
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • This is perfect! – ruohola Dec 03 '19 at 15:45
  • This is not shadowing. This is re-assignment. It's equivalent to `a = a*a; b = b*b;`. – KevinZ Dec 06 '19 at 03:32
  • @KevinZ I never used the word shadowing. In fact, I used reassigned: *That will create a a std::tuple of references to the parameters and allow you to **reassign** the pair to the elements it was created from.* – NathanOliver Dec 06 '19 at 13:23
  • @KevinZ Yeah good call, I used the word shadowing too loosely, reassigning is completely fine also for me here. – ruohola Jan 10 '20 at 16:09