Применяется ли продление времени жизни временного объекта с помощью ссылок const
к элементам временных контейнеров, как, например, в случае ссылки r в программе ниже?
Гарантируется ли это стандартом C++?
Я читал, что такое продление срока службы применяется к подобъектам полных объектов, но могут ли элементы стандартных контейнеров квалифицироваться как подобъекты соответствующего контейнера?
#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;
}
Между объектом и ссылкой, возвращаемой методом этого объекта, априори нет пожизненной связи. Представьте себе int global_int; struct my_map { int& operator[](std::string) { return global_int; } }
. Возвращаемое значение operator[]
— это ссылка на глобальный объект, время жизни которого не привязано к времени жизни my_map
.
Временное значение, возвращаемое функцией, привязано к неявному параметру this
, и время жизни временного значения не продлевается.
@CoryKramer, к сожалению, я застрял на gcc 9.5, который, похоже, не выдает никаких предупреждений об этом с помощью -Wall -Wextra. Однако большое спасибо за указание на Godbolt, чтобы я мог опробовать такие выражения на каком-нибудь минимальном примере и оценить их правильность!
@DrewDormann, к сожалению, я использую gcc 9.5, который не выдает таких предупреждений... В любом случае большое спасибо!
Нет, r
недействителен. r
становится недействительной ссылкой на временную ссылку, как только оператор r = func()["foo"];
завершается.
Вот доказательство концепции, основанное на вашем примере кода:
#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?
}
Когда я запускаю следующую программу, я получаю такие результаты:
~Foo(42)
~Foo(42)
r.value = -572662307 # GARBAGE OUTPUT
-572662307
— это 0xdddddddd
в шестнадцатеричном формате, который отладка Visual Studio присваивает удаленной памяти, которая не была освобождена. Так что это явный признак того, что r
определенно недействителен.
Это будет допустимо в вашем коде и выведет ожидаемое значение 42:
std::cout << "func['foo'] = " << func()["foo"] << std::endl;
В конце вы наверняка имели в виду func()["foo"]
, а не func["foo"]
.
@Юджин, хороший улов. Зафиксированный.
В данном конкретном примере это старая добрая висячая ссылка godbolt.org/z/j7vvhdoTf «предупреждение: объект, поддерживающий указатель, будет уничтожен в конце полного выражения [-Wdangling-gsl]»