Продлевает ли член класса Const Reference временный срок?
Почему это:
#include <string>
#include <iostream>
using namespace std;
class Sandbox
{
public:
Sandbox(const string& n) : member(n) {}
const string& member;
};
int main()
{
Sandbox sandbox(string("four"));
cout << "The answer is: " << sandbox.member << endl;
return 0;
}
Дайте вывод:
Ответ:
Вместо:
Ответ: четыре
7 ответов
Только местный const
ссылки продлевают срок службы.
Стандарт определяет такое поведение в §8.5.3/5, [dcl.init.ref], разделе об инициализаторах ссылочных объявлений. Ссылка в вашем примере привязана к аргументу конструктора n
и становится недействительным, когда объект n
обязательно выходит за рамки.
Увеличение продолжительности жизни не транзитивно через аргумент функции. §12.2/5 [класс.время]:
Второй контекст, когда ссылка связана с временным. Временный объект, к которому привязана ссылка, или временный объект, являющийся полным объектом для подобъекта, к которому привязан временный объект, сохраняется в течение всего срока действия ссылки, за исключением случаев, указанных ниже. Временная привязка к элементу ссылки в ctor-initializer конструктора (§12.6.2 [class.base.init]) сохраняется до выхода из конструктора. Временная привязка к ссылочному параметру в вызове функции (§5.2.2 [expr.call]) сохраняется до завершения полного выражения, содержащего вызов.
Вот самый простой способ объяснить, что произошло:
В main() вы создали строку и передали ее в конструктор. Этот экземпляр строки существовал только внутри конструктора. Внутри конструктора вы назначили член, чтобы указывать непосредственно на этот экземпляр. Когда область видимости покинула конструктор, экземпляр строки был уничтожен, а член указал на строковый объект, которого больше не было. Если указатель Sandbox.member на ссылку за пределами ее области действия, эти внешние экземпляры не будут удерживаться в области действия.
Если вы хотите исправить свою программу для отображения желаемого поведения, внесите следующие изменения:
int main()
{
string temp = string("four");
Sandbox sandbox(temp);
cout << sandbox.member << endl;
return 0;
}
Теперь temp будет выходить из области видимости в конце main(), а не в конце конструктора. Однако это плохая практика. Ваша переменная-член никогда не должна быть ссылкой на переменную, существующую вне экземпляра. На практике вы никогда не знаете, когда эта переменная выйдет из области видимости.
Я рекомендую определить Sandbox.member как const string member;
Это скопирует данные временного параметра в переменную-член вместо назначения переменной-члена в качестве самого временного параметра.
Технически говоря, эта программа не обязана выводить что-либо на стандартный вывод (для начала это буферизованный поток).
cout << "The answer is: "
немного испустит"The answer is: "
в буфер стандартного вывода.Тогда
<< sandbox.member
бит предоставит висячую ссылку вoperator << (ostream &, const std::string &)
, который вызывает неопределенное поведение.
Из-за этого ничего не гарантируется. Программа может работать на вид нормально или может зависнуть даже без очистки stdout - это означает, что текст "Ответ: " не появится на вашем экране.
Из других ответов ясно, что члены класса не продлевают жизнь временного объекта за пределами вызова конструктора. Однако есть случаи, когда ваш API может "безопасно" предполагать, что все
const&
объекты, переданные в класс, не будут временными объектами, а будут ссылками на объекты с хорошей областью видимости.
Если вы не хотите создавать копии, что вы можете сделать, чтобы UB не проникал в ваш код? Лучший инструмент, который у вас есть, - это оправдать предположение, что
std::string const&
переданные конструктору не являются временными, объявив как удаленную перегрузку, которая принимает такие временные объекты:
#include <string>
#include <iostream>
using namespace std;
class Sandbox
{
public:
Sandbox(const string& n) : member(n) {}
Sandbox(string&&) = delete;
// ^^^ This guy ;)
const string& member;
};
int main()
{
Sandbox sandbox(string("four"));
// Detect you're trying ^^^ to bind a
// reference to a temporary and refuse to compile
return 0;
}
Простой ответ: вы имеете в виду что-то, что исчезло. Следующее будет работать
#include <string>
#include <iostream>
using namespace std;
class Sandbox
{
public:
const string member = " ";
Sandbox(const string& n) : member(n) {}//a copy is made
};
int main()
{
Sandbox sandbox(string("four"));
cout << "The answer is: " << sandbox.member << endl;
return 0;
}
Потому что ваша временная строка вышла из области видимости после того, как вернулся конструктор Sandbox, и стек, занятый ею, был освобожден для некоторых других целей.
Как правило, вы никогда не должны сохранять ссылки в долгосрочной перспективе. Ссылки хороши для аргументов или локальных переменных, но не для членов класса.
Вы имеете в виду то, что исчезло. Следующее будет работать
#include <string>
#include <iostream>
class Sandbox
{
public:
const string member = " "; //default to whatever is the requirement
Sandbox(const string& n) : member(n) {}//a copy is made
};
int main()
{
Sandbox sandbox(string("four"));
std::cout << "The answer is: " << sandbox.member << std::endl;
return 0;
}