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