Does prolonging the lifetime of a temporary object through const
references also apply for elements of temporary containers, such as in the case of reference r in the program below?
Is that guaranteed by the C++ standard?
I've read that this sort of lifetime extension does apply for subobjects of complete objects, but do elements of standard containers qualify as subobjects of their respective container?
#include <unordered_map>
#include <string>
#include <iostream>
using my_map = std::unordered_map<std::string, int>;
my_map func() {
return my_map{{"foo", 42}};
}
int main() {
const auto& r = func()["foo"];
std::cout << "r = " << r << std::endl; // Is r valid here?
return 0;
}
No, r
is not valid. r
becomes an invalid reference to a temporary as soon as the statement is r = func()["foo"];
is over.
Here's a proof of concept that extends upon your example code:
#include <unordered_map>
#include <string>
#include <iostream>
class Foo {
public:
int value;
Foo() : value(0) {
value = 0;
}
Foo(int n) : value(n) {
}
~Foo() {
std::cout << "~Foo(" << value << ")\n";
}
};
using my_map = std::unordered_map<std::string, Foo>;
my_map func() {
return my_map{ {"foo", 42} };
}
int main() {
const auto& r = func()["foo"];
std::cout << "r.value = " << r.value << std::endl; // Is r valid here?
}
When I run the following program, I get these results:
~Foo(42)
~Foo(42)
r.value = -572662307 # GARBAGE OUTPUT
-572662307
is 0xdddddddd
in hex, which is what Visual Studio's debug assigns to deleted memory that hasn't been reclaimed. So it's a strong indication that r
is definitely invalid.
This would be valid in your code and would print the expected value of 42:
std::cout << "func['foo'] = " << func()["foo"] << std::endl;