Как компилятор определяет, когда это безопасно для RVO? И нет, я имею в виду не rvalue, а lvalue — если я правильно понимаю, RVO работает, «перенаправляя» целевой адрес в метод, поэтому он возвращается к целевому адресу вместо адреса временного и затем копирует / назначить на цель.
Но как компилятор узнает, что безопасно выполнять RVO? Что если в lvalue уже есть некоторые данные, включая динамически распределяемый ресурс? В таком случае RVO может привести к утечке ресурса. Может быть, есть некоторые правила, которые определяют, применимо ли это для выполнения оптимизации или придерживаться использования копирования или назначения?
RVO может только инициализировать новый объект, но не переназначить существующий объект. Итак, в этом случае:
Thing thing = make_thing();
адрес thing
передается функции, которая инициализирует ее на месте.
В этом случае:
thing = make_thing();
RVO (в общем) создает временный, который затем присваивается thing
, Не будет никаких утечек или подобных проблем, пока тип правильно назначается. Поскольку это временный Rvalue, его можно перенести, что может быть более эффективным, чем копирование. Если тип является тривиально присваиваемым, то это присваивание также может быть исключено — компилятор будет знать, так ли это, когда он выбирает, как вызвать функцию.
Оптимизация возвращаемого значения является частным случаем исключения копии. Это может произойти в следующей ситуации, как описано в стандарте:
в операторе возврата в функции с типом возврата класса, когда выражение является именем энергонезависимого автоматического объекта (отличного от параметра функции или оператора catch) с тем же cv-неквалифицированным типом, что и тип возврата функции, операцию копирования / перемещения можно опустить, создав автоматический объект непосредственно в возвращаемое значение функции
Нет причин, по которым это может привести к утечке памяти. Если класс выполняет некоторое динамическое размещение в своем конструкторе, это произойдет, когда объект будет создан непосредственно в возвращаемое значение функции.
В ответ на ваш комментарий (где foo1
а также foo2
обе конструкции T
объекты и вернуть их):
T a = foo1();
a = foo2();
Здесь мы рассматриваем не только RVO, но и другой тип исключения копий, возникающий при попытке создать объект из временного объекта.
В первой строке можно удалить две копии / ходы:
foo1
a
То есть объект построен в foo1
может быть непосредственно создан в местоположении a
, Если конструктор динамически выделяет некоторый объект, это будет сделано только один раз, для a
объект.
Во второй строке может быть исключено единственное копирование / перемещение — только возврат из функции. Таким образом, объект, который foo2
конструкции будут созданы непосредственно в возвращаемом значении функции, тогда это будет Копировать / переместить назначенный в a
, Копирование / перемещение заданий не отменяются.
Затем это зависит от оператора назначения копирования / перемещения, чтобы гарантировать, что исходный выделенный ресурс был безопасно отброшен, а единственным оставшимся ресурсом является тот, который был создан внутри foo2
,