Как уже упоминал Энтони, стандарт запрещает копировать elision из аргумента функции в возвращение той же самой функции. Логическое обоснование этого решения заключается в том, что удаление копии (и перемещение перемещения) - это оптимизация, с помощью которой два объекта в программе объединяются в одну и ту же область памяти, то есть копия удаляется, если оба объекта являются одним. Ниже приведена (частичная) стандартная цитата, за которой следует ряд обстоятельств, при которых допускается копирование, которые не включают этот конкретный случай.
Так чем же отличается этот конкретный случай? Различие состоит в основном в том, что тот факт, что между исходным и скопированным объектами есть вызов функции, и вызов функции подразумевает наличие дополнительных ограничений, в частности, соглашения о вызовах.
Учитывая функцию T foo( T )
и пользователя, вызывающего T x = foo( T(param) );
, в общем случае с отдельной компиляцией, компилятор создаст объект $tmp1
в месте, в котором соглашение о вызовах требует, чтобы первый аргумент был. Затем он вызовет функцию и инициализирует x
из оператора return. Вот первая возможность удаления копии: аккуратно поместив x
в место, где находится возвращенный временный объект, x
и возвращенный объект из foo
становятся единым объектом, и эта копия удаляется. Все идет нормально. Проблема в том, что соглашение о вызовах в общем случае не будет иметь возвращенный объект и параметр в одном и том же месте, и поэтому $tmp1
и x
не могут быть одним местом в памяти.
Не видя определения функции, компилятор не может знать, что единственная цель аргумента функции - служить оператором возврата, и поэтому он не может исключить эту дополнительную копию. Можно утверждать, что если функция inline
, то у компилятора будет отсутствующая дополнительная информация, чтобы понять, что временное значение, используемое для вызова функции, возвращаемое значение и x
представляют собой один объект. Проблема заключается в том, что эту конкретную копию можно удалить только в том случае, если код действительно встроенный (не только если он помечен как inline
, но фактически встроен ) Если требуется вызов функции, то копия не может быть опущены. Если бы стандарт позволял исключать эту копию при вставке кода, это означало бы, что поведение программы будет отличаться из-за компилятора, а не из-за кода пользователя - ключевое слово inline
не приводит к принудительному встраиванию, оно только означает, что множественные определения одной и той же функции не представляют собой нарушение ODR.
Обратите внимание, что если переменная создана внутри функции (по сравнению с переданной в нее), как в: T foo() { T tmp; ...; return tmp; } T x = foo();
, то обе копии могут быть исключены: по состоянию на * 1027 ограничения нет должен быть создан (это не входной или выходной параметр функции, поэтому компилятор может переместить его куда угодно, включая местоположение возвращаемого типа, и на вызывающей стороне x
может быть, как в предыдущем примере, аккуратно расположен в том же самом операторе возврата, что в основном означает, что tmp
, оператор возврата и x
могут быть одним объектом.
Что касается вашей конкретной проблемы, если вы прибегаете к макросу, код встроен, нет никаких ограничений на объекты, и копия может быть удалена. Но если вы добавите функцию, вы не сможете удалить копию из аргумента в оператор return. Так что просто избегайте этого. Вместо использования шаблона, который будет перемещать объект, создайте шаблон, который будет создавать объект:
template <typename T, typename... Args>
T create( Args... x ) {
return T( x... );
}
И эта копия может быть удалена компилятором.
Обратите внимание, что я не имел дело со строительством ходов, так как вы, кажется, обеспокоены стоимостью строительства даже ходов, хотя я считаю, что вы лаете не на то дерево. Учитывая мотивирующий реальный сценарий использования, я вполне уверен, что люди здесь предложат пару эффективных идей.
12,8 / 31 * * тысяча сорок шесть
При соблюдении определенных критериев реализация может опустить конструкцию копирования / перемещения объекта класса, даже если конструктор копирования / перемещения и / или деструктор для объекта имеют побочные эффекты. В таких случаях реализация рассматривает источник и цель пропущенной операции копирования / перемещения просто как два разных способа обращения к одному и тому же объекту, и уничтожение этого объекта происходит в более поздние времена, когда два объекта были бы уничтожен без оптимизации.