Dean
Dean

Reputation: 6958

std::tie vs std::make_tuple

This code compiles but I'm wondering which version should be preferred:

#include <iostream>
#include <tuple>
using namespace std;

tuple<int, int, int> return_tuple1() {
    int a = 33;
    int b = 22;
    int c = 31;
    return tie(a, b, c);
}

tuple<int, int, int> return_tuple2() {
    int a = 33;
    int b = 22;
    int c = 31;
    return make_tuple(a, b, c);
}

int main() {
    auto a = return_tuple1();
    auto b = return_tuple2();
    return 0;
}

since the function is returning a tuple by value there shouldn't be any problem in using std::tie right? (i.e. no dangling references)

Upvotes: 26

Views: 13559

Answers (3)

Richard Hodges
Richard Hodges

Reputation: 69942

Be very careful with std::tie. Returning a tie is logically equivalent to returning a reference, with all of the caveats that come with it.

Logically, these three are equivalent:

int& foo();
std::reference_wrapper<int> foo();
std::tuple<int&> foo();

and this:

int a = 10;
return std::tie(a);

is equivalent to this:

int a = 10;
return std::ref(a);

because it produces one of these:

std::tuple<int&>

In your example you are saved by the return value's implicit conversion. However, replacing the return type with auto reveals the logic error:

#include <iostream>
#include <tuple>
using namespace std;

auto return_tuple1() {  // function name is now lying
    int a = 33;         // it should be return_chaos()
    int b = 22;
    int c = 31;
    return tie(a, b, c);
}

auto return_tuple2() {
    int a = 33;
    int b = 22;
    int c = 31;
    return make_tuple(a, b, c);
}

int main() {
    auto a = return_tuple1(); // uh-oh...

    auto b = return_tuple2();

    std::get<0>(a); // undefined behaviour - if you're lucky you'll get a segfault at some point.
    std::get<0>(b); // perfectly ok
    return 0;
}

Upvotes: 33

NathanOliver
NathanOliver

Reputation: 181068

tuple2 should be more efficient. tie does create a tuple but the thing you have to remember about tie is that it does not make a tuple of type Args... but instead makes a tuple of type Args&... meaning your return type and your tuple do not match. That means you need to copy from the tied tuple to the return tuple.

In the second example you are returning a tuple with the same type as the return type so you can return that directly. You do not have to copy there.

Upvotes: 3

David Haim
David Haim

Reputation: 26546

std::tie won't do what you think it does.
std::tie returns a tuple of references to the elements passed, so in return_tuple1(), what actually happens is :

tuple<int, int, int> return_tuple1() {
    int a = 33;
    int b = 22;
    int c = 31;
    return std::tuple<int&,int&,int&>(a,b,c);
}

then, the return type tuple<int, int, int> builds itself from std::tuple<int&,int&,int&>.

now, the compiler might optimize this construction away, but I wouldn't bet on that. use std::make_tuple as it is the right tool for that task.

Upvotes: 18

Related Questions