#TIL Давно хотел в плюсах посмотреть на проблему при возвращении const'ной ссылки из метода/функции в C++. Тема старая, но всё никак руки не доходили.
Есть следующий код (очередной искусственный пример для демонстрационных целей):
#include <iostream>
#include <string>
#include <cstdlib>
const std::string& takeString(int value, const std::string& str1, const std::string& str2, const std::string& str3)
{
if (value < 0)
{
return str1;
}
if (value > 0)
{
return str2;
}
return str3;
}
int main(int argc, char* argv[])
{
if (argc != 2)
{
std::exit(1);
}
int value = std::atoi(argv[1]);
std::string s1 = "one";
std::string s2 = "two";
const std::string& s = takeString(value, s1, s2, "three");
std::cout << "s: " << s << "\n";
return 0;
}
Компилируем и выполняем с разными опциями:
$ ./example -10
s: one
$ ./example 10
s: two
Если же вызвать с опцией 0:
$ ./example 0
результат будет неопределён (конкретно у меня был выведен "мусор" на экран). Если скомпилировать программу с адрес санитайзером (-fsanitize=address), то при вызове example с опцией 0 получим следующую ошибку:
ERROR: AddressSanitizer: stack-use-after-scope
В чём суть проблемы? При внимательном рассмотрении кода видно, что вызов функции takeString на самом деле будет следующим:
const std::string& s = takeString(value, s1, s2, std::string("three"));
т.е. при передаче третьим аргуметом литерала "three" будет сконструирован временный объект типа std::string, который разрушится перед выходом из функции takeString, что логично. Продление времени жизни объекта за счет const std::string& s здесь не будет выполняться.
В результате, когда функция takeString возвращает str3, будет получена висячая ссылка. В случае, когда вызов takeString возвращает s1 или s2 никаких проблем нет, т.к. они разрушатся при выходе из main, их можно спокойно использовать после вызова takeString (в std::cout).
Какой вывод нужно сделать? Не нужно возвращать из функции/метода по константной ссылке параметр типа константная ссылка, да и любой другой временный объект, например:
const int& badFunction()
{
int value = 42;
return value;
}
Правда тут мне компилятор выдал предупреждение:
warning: reference to stack memory associated with local variable 'value' returned [-Wreturn-stack-address]