Есть ли какая-либо разница между этими формами: ReturnType vs ReturnType

#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() возвращает, потому что копия всегда создается (по крайней мере, концептуально).