Гарантируется ли перемещение объекта при его возврате?
Я знаю, что при передаче объекта по значению в функцию конструктор перемещения всегда вызывается, если он есть, при условии, что копия не исключена. Как насчет возврата объекта по значению?
Например, скажем, у нас есть класс Foo
который имеет конструктор перемещения, и у нас есть функция, которая возвращает Foo
объект.
Foo g() {
Foo f;
// do something with f
return f;
}
Если мы предположим, что RVO не существует, гарантированно будет вызван конструктор перемещения?
Обновление: я предполагаю, что я не показал свое намерение ясно. Я просто хочу знать, что в худшем случае я могу переместить объект, не скопировав его. Либо RVO, либо NRVO случается, я счастлив. И я должен также сказать, что конструктор перемещения и назначение перемещения не удаляются и должным образом реализованы.
3 ответа
Да. Смотрите [class.copy] p32
Когда критерии для исключения операции копирования выполнены или будут выполнены, за исключением того факта, что исходный объект является параметром функции, а копируемый объект обозначен lvalue, разрешением перегрузки для выбора конструктора для копирования является сначала выполняется, как если бы объект был обозначен как rvalue. Если не удается разрешить перегрузку или если тип первого параметра выбранного конструктора не является rvalue-ссылкой на тип объекта (возможно, cv-квалифицированный), разрешение перегрузки выполняется снова, рассматривая объект как lvalue. [ Примечание: это двухэтапное разрешение перегрузки должно выполняться независимо от того, будет ли выполнено копирование. Он определяет конструктор, который будет вызван, если elision не выполняется, и выбранный конструктор должен быть доступен, даже если вызов исключен. - конец примечания ]
Правило состоит в том, что всякий раз, когда разрешение на копирование разрешено, но не происходит, конструктор перемещения будет использоваться, если он доступен, в противном случае будет использоваться конструктор копирования.
Точное поведение определяется [class.copy]/32
:
Когда критерии для исключения операции копирования выполнены или будут выполнены, за исключением того факта, что исходный объект является параметром функции, а копируемый объект обозначен lvalue, разрешением перегрузки для выбора конструктора для копирования является сначала выполняется, как если бы объект был обозначен как rvalue. Если не удается разрешить перегрузку или если тип первого параметра выбранного конструктора не является rvalue-ссылкой на тип объекта (возможно, cv-квалифицированный), разрешение перегрузки выполняется снова, рассматривая объект как lvalue.
В этом случае, поскольку возвращаемое значение имеет имя (f
), будет применяться NRVO (оптимизация именованного значения).
Таким образом, технический ответ, основанный только на формулировках, заключается в том, что отсутствие RVO не помешает исключению копирования, поскольку NRVO все еще может это позволить.
Кроме того, я считаю, что выбор между перемещением / копией возвращаемого значения может / будет зависеть от определения Foo - определенно бывают моменты, когда оно будет скопировано, а не перемещено, например, если вы явно удалили конструктор перемещения и операторы перемещения, или вы не определили конструкцию / назначение перемещения, и не допускается их неявный синтез.
Редактировать: [отвечает на отредактированный вопрос]: Наличие конструктора перемещения все еще не гарантирует, что результат будет перемещен. Одним из очевидных примеров будет то, что вы удалили оператор присваивания перемещения и присваивали результат (а не использовали его для инициализации). В этом случае удаленный оператор присваивания перемещения предотвратит перемещение возвращаемого значения.
Чтобы ответить на то, к чему вы, возможно, стремились, общее правило заключается в том, что перемещение будет выполнено, если это возможно, и оно вернется к копированию тогда и только тогда, когда что-то помешает перемещению результата.