Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::tie vs std::make_tuple

Tags:

c++

tuples

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)

like image 415
Dean Avatar asked Dec 01 '16 15:12

Dean


People also ask

What does std :: tie do in C++?

std::tie. Creates a tuple of lvalue references to its arguments or instances of std::ignore.

What is Make_tuple?

std::make_tupleCreates a tuple object, deducing the target type from the types of arguments.

Does Make_tuple copy?

Yes it does. A tuple holds variables by value, so it must copy the values into the tuple. If you want only their references copied, use pointers instead, i.e., boost::make_tuple(&objA,&objB) .

Is std :: tuple a container?

The new std::array and std::tuple containers provide developers with additional ways to manage structured data efficiently.


2 Answers

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;
}
like image 136
Richard Hodges Avatar answered Oct 23 '22 02:10

Richard Hodges


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.

like image 32
David Haim Avatar answered Oct 23 '22 02:10

David Haim