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>.
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.
This is not UB.
but as far as I know
std::tuple<int&, int>is not the same type asstd::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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With