Reputation: 6958
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
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
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
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