#c #reference #return-value #return-type
#c #ссылка #возвращаемое значение #возвращаемый тип
Вопрос:
Рассмотрим эти бесплатные автономные функции:
std::vector<int>amp; f(); //reference
std::vector<int> g(); //value
/*const*/ std::vector<int>amp; f1 = f(); //reference
std::vector<int> f2 = f(); //value
/*const*/ std::vector<int>amp; g1 = g(); //reference
std::vector<int> g2 = g(); //value
Есть ли какая-либо разница между :
-
f () и g(). Это простой вопрос, но все же я хотел бы услышать некоторые подробные комментарии к ним, поскольку это могло бы помочь понять ответ на следующие вопросы.
-
f1 и f2. Будут ли они одним и тем же исходным объектом из f (), или f2 будет копией оригинала? Раскомментирование
const
будет иметь какое-либо значение? -
g1 и g2. Будут ли они одним и тем же исходным объектом из g (), или g2 будет копией оригинала? Раскомментирование
const
будет иметь какое-либо значение?
Что, если f()
и g()
являются функциями-членами, и каждая возвращает данные-члены, а не какую-либо локальную переменную? Будет ли это иметь какое-либо значение в ответе на вышеуказанные вопросы?
Пожалуйста, попробуйте включить все подводные камни и важные моменты в свой ответ и не рассматривайте RVO или любую другую оптимизацию компилятором. Я хочу знать, что такое C , а не то, что делают компиляторы. Если вы говорите об оптимизации, пожалуйста, явно укажите это, чтобы я не смешивал возможности языка с возможностями компилятора.
Комментарии:
1. Исключение копирования — это языковая функция, она явно вызвана в стандарте.
2. @Ben: Это обязательно, или компилятор необязательно может это сделать?
3. @Nawaz: При определенных условиях не указано, вызывается ли конструктор копирования (или конструктор перемещения). Это должно быть вызвано в стандарте, поскольку устранение изменяет наблюдаемое поведение программы, эта оптимизация не была бы разрешена в соответствии с правилом «как если бы».
4. @Ben:
as-if
правило? это означает, что это не обязательно, и это необязательно выполняется компиляторами, верно? Компилятор, который не выполняетсяcopy-elision
, все еще можно назвать соответствующим стандарту.5. @Nawaz: Конечно. Но программа, которая полагается на отсутствие исключения копирования, не является четко определенной, точно так же, как программа, которая дважды изменяет переменную между точками последовательности.
Ответ №1:
f()
возвращает ссылку на объект; возврат из него не копирует какой-либо объект. g()
возвращает копию объекта, по крайней мере концептуально.
std::vector<int>amp; f1 = f(); //reference
f1
ссылается на объект, на который f()
возвращена ссылка. Копии не создаются. Const-квалификация ссылки здесь не имеет значения (что касается копирования; очевидно, это влияет на то, что можно сделать с объектом).
std::vector<int> f2 = f(); //value
f2
является копией объекта, на который f()
возвращена ссылка.
std::vector<int>amp; g1 = g(); //reference
Это недопустимо. Неконстантная ссылка не может быть привязана к временному объекту.
Если ссылка имеет значение const, то эта строка фактически совпадает со следующей строкой: создается копия объекта, возвращаемого g()
, ссылка привязывается к этой копии, и этой копии присваивается время жизни ссылки (она уничтожается, когда ссылка «уничтожается»).
std::vector<int> g2 = g(); //value
g2
является копией объекта, возвращаемого g()
. Будет ли сделана копия (и сколько копий может быть сделано), зависит от оптимизации компилятора.
Что, если
f()
иg()
являются функциями-членами, и каждая возвращает данные-члены, а не какую-либо локальную переменную?
Если f()
возвращает ссылку на локальную переменную, то программа неверна и выдает неопределенное поведение при попытке использовать ссылку, потому что объект, на который ссылаются, перестает существовать при возврате функции.
Если f()
возвращает ссылку на переменную-член, динамически выделяемый объект или объект со статическим или потоковым локальным сроком хранения, то ссылка действительна в течение всего срока службы этого объекта (или на другой объект того же типа, созданный в том же месте в памяти, что и объект, на который была возвращена ссылка, хотя полезность этого ограничена несколькими избранными сценариями).
Не имеет значения, что g()
возвращает, потому что копия всегда создается (по крайней мере, концептуально).