15

Is it possible to assign the members of a pair without creating a temporary object?

#include <tuple>
using namespace std;

pair< bool, int > foo()
{
    return make_pair( false, 3 );
}

int main()
{
    int x;
    bool y;

    auto z = foo();
    x = z.second;
    y = z.first;

    return 0;
}

In the above code, the object auto z is needed to "hold" the pair before dissecting it, but its creation might be expensive in the actual code.

Hector
  • 2,464
  • 3
  • 19
  • 34

3 Answers3

30

Yes; std::tie was invented for this:

#include <tuple>
#include <iostream>

std::pair<bool, int> foo()
{
    return std::make_pair(false, 3);
}

int main()
{
    int x;
    bool y;

    std::tie(y, x) = foo();
    std::cout << x << ',' << y << '\n';
}

// Output: 3,0

(live demo)

Of course you are still going to have a temporary object somewhere (modulo constant optimisations), but this is the most direct you can write the code unless you initialise x and y directly from their eventual values rather than first creating a pair inside foo().

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • @jxh You are returning a temporary by value, the compiler will already perform RVO. What Lightness is referring to (which they are correct about) is that within `std::tie`, they must instantiate the `std::pair` to be able to pull out the `first` and `second` to assign to your variables. `std::tie` is simply a convenience function in that regard. – Cory Kramer Aug 03 '15 at 17:13
  • @CoryKramer: RVO should mean `tie`'s constructor can avoid the copy too. The only "copy" should be the one created by `make_pair`. – jxh Aug 03 '15 at 17:17
  • Theoretically, at least in this contrived case, I'd expect the entire program to just become a few instructions to stream `3` and `0` to _stdout_. It can _all_ be optimised away. If your pair is more complex then you have RVO and move semantics to rely on. Don't worry. – Lightness Races in Orbit Aug 03 '15 at 17:17
10

The C++17 already let you to use the Structured binding declaration syntax:

#include <iostream>

std::pair<bool, int> foo() {
    return std::make_pair(false, 3);
}

int main() {
    auto [y, x] = foo();               //Structured binding attribution
    std::cout << x << ',' << y << '\n';
}
TheArchitect
  • 1,160
  • 4
  • 15
  • 26
  • 2
    The official name for this gizmo is a [structured binding declaration](https://en.cppreference.com/w/cpp/language/structured_binding). – Quentin Oct 04 '19 at 14:15
  • 1
    It seems that `bool y; int x; [y,x] = foo();` does not work. Variables must not already defined? – Chameleon Sep 08 '20 at 13:46
  • @Chameleon fool() is a function that return a pair of variables (one bool and one int). Here it is the definition of them. When you write auto[y, x] = foo() the compiler knows that y is a bool and x is a int because that is exactly what foo() returns. – TheArchitect Sep 08 '20 at 15:57
  • @TheArquitect I understand that, but if `x` & `y` must be already defined (defined before, assigned inside `if` or `switch`), does not work. In this case `std::tie` does the job. – Chameleon Sep 09 '20 at 08:57
  • 2
    @Chameleon Basically the differences is tie create the values temporarily and you attribute it (or not) to an existent variable. This new format auto[x, y] just create the variables directly. See this interesting question I found: https://stackoverflow.com/questions/40241335/structured-binding-to-replace-stdtie-abuse – TheArchitect Sep 09 '20 at 15:21
3

I agree wtih The Architect. However, if you are using C++17, then you can also drop the make_pair in favor of a more readable structured binding in the return statement.

#include <iostream>

std::pair<bool, int> foo() {
    return {false, 3}; // <- structured binding
}

int main() {
    auto [y, x] = foo();               // structured binding attribution
    std::cout << x << ',' << y << '\n';
}
Edward Gaere
  • 1,092
  • 6
  • 11