Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extending lifetime of std::tuple<int&,int> by assigning it to const std::tuple<int, int>&

I was using the std::tuple class and found something I would say is rather unexpected behavior.

Consider the code:

#include <iostream>
#include <tuple>

int i = 20;
std::tuple<int&, int> f() {
    return std::tuple<int&, int>(i, 0);
}

int main() {
    const std::tuple<int, int>& t = f();
    int j = ++i;
    std::cout << std::get<0>(t) << "\n";
}

This seems to compile and print 20 on all major compilers. Is this standard conforming, or undefined behavior as the two types are different? I know that one can extend the lifetime of a temporary by assigning it to const T&, but as far as I know std::tuple<int&, int> is not the same type as std::tuple<int, int>.

like image 913
Mestkon Avatar asked Jan 18 '19 14:01

Mestkon


2 Answers

This is well defined behavior.

const std::tuple<int, int>& t = f();

is not giving you a reference to the tuple you created in f() because they have different types. Instead what happens is a temporary std::tuple<int, int> is created from f()'s return and then that temporary is bound to t. Since this is a copy you get the value of i at that point in time and are no longer coupled to it.

Had you used

const std::tuple<int&, int>& t = f();

then 21 would have been printed since you would still have a reference to i in the tuple.

like image 71
NathanOliver Avatar answered Oct 07 '22 20:10

NathanOliver


This is not UB.

but as far as I know std::tuple<int&, int> is not the same type as std::tuple<int, int>.

Yes, and reference can't bind to object with different type directly. Given const std::tuple<int, int>& t = f();, the returned std::tuple<int&, int> will be converted to std::tuple<int, int> implicitly, which is a temporary std::tuple<int, int>. Then the temporary is bound to t and gets lifetime extended to the lifetime of t.

like image 6
songyuanyao Avatar answered Oct 07 '22 20:10

songyuanyao