Должен ли временный T в качестве параметра вызывать T(const T&) или T(T&&) в C++11?
Итак, код сначала:
#include <iostream>
#include <utility>
struct X{
int i;
void transform(){}
X() :i(0){std::cout<<"default\n";}
X(const X& src): i(src.i){std::cout<<"copy\n";}
X(X&& msrc) :i(msrc.i){msrc.i=0;std::cout<<"move\n";}
};
X getTransform(const X& src){
X tx(src);
tx.transform();
return tx;
}
int main(){
X x1;// default
X x2(x1); // copy
X x3{std::move(X{})}; // default then move
X x41(getTransform(x2)); // copy in function ,then what?
X x42(std::move(getTransform(x2))); // copy in funciton, then move
X x51( (X()) );//default, then move? or copy?
// extra() for the most vexing problem
X x52(std::move(X())); //default then move
std::cout<<&x41<<"\t"<<&x51<<std::endl;
}
Затем выход из cygwin + gcc 4.8.2 с включенными функциями C++11:
default
copy
default
move
copy
copy
move
default
default
move
0x22aa70 0x22aa50
То, что я не совсем понимаю, это линия для x41 и x51. Для x41, должен ли временный код, возвращаемый из вызова функции, вызывать конструктор перемещения или копию? Тот же вопрос для x51. Второй вопрос заключается в том, что, глядя на выходные данные, конструкции x41 и x51 не вызывают никаких определенных конструкторов, но объекты явно создаются, поскольку они находятся в памяти. Как это могло произойти?
Спасибо!
3 ответа
Неназванный объект соответствует &&
лучше чем const&
естественно.
В противном случае семантика перемещения не будет работать.
Теперь к вашим конструкторам по умолчанию /copy/move-конструкторам меньше обращений, чем можно наивно ожидать, поскольку существует специальное правило, разрешающее удаление копий без учета наблюдаемого поведения (которое в противном случае должно быть сохранено оптимизацией):
12.8 Копирование и перемещение объектов § 31
При соблюдении определенных критериев реализация может опустить конструкцию копирования / перемещения объекта класса, даже если конструктор копирования / перемещения и / или деструктор для объекта имеют побочные эффекты. В таких случаях реализация рассматривает источник и цель пропущенной операции копирования / перемещения просто как два разных способа обращения к одному и тому же объекту, и уничтожение этого объекта происходит в более поздние времена, когда два объекта были бы уничтожено без оптимизации.123 Это исключение операций копирования / перемещения, которое называется разрешением копирования, разрешено при следующих обстоятельствах (которые могут быть объединены для удаления нескольких копий):
Тем не менее, если он возвращается из функции и напрямую используется для инициализации объекта того же типа, это перемещение будет опущено.
- в операторе возврата в функции с типом возврата класса, когда выражение является именем энергонезависимого автоматического объекта (кроме параметра функции или предложения catch) с тем же типом cv-unqualified, что и тип возврата функции операцию копирования / перемещения можно опустить, создав автоматический объект непосредственно в возвращаемое значение функции.
- когда временный объект класса, который не был связан со ссылкой (12.2), будет скопирован / перемещен в объект класса с тем же типом cv-unqualified, операция копирования / перемещения может быть опущена путем создания временного объекта непосредственно в цель пропущенного копирования / перемещения.
- [... еще 2 для обработки исключений]
Итак, пройдемся по вашему списку:
X x1;// default
// That's right
X x2(x1); // copy
// Dito
X x3{std::move(X{})}; // default then move
// Yes. Sometimes it does not pay to call `std::move`
X x41(getTransform(x2)); // copy in function ,then what?
// Copy in function, copy to output, move-construction to x41.
// RVO applies => no copy to output, and no dtor call for auto variable in function
// Copy ellision applies => no move-construction nor dtor of temporary in main
// So, only one time copy-ctor left
X x42(std::move(getTransform(x2))); // copy in funciton, then move
// `std::`move` is bad again
X x51( (X()) );//default, then move? or copy? // extra() for the most vexing problem
// Copy-elision applies: default+move+dtor of temporary
// will be optimized to just default
X x52(std::move(X())); //default then move
// And again `std::`move` is a pessimization
Я думал, используя static_cast
может избежать связывания временного, что означает, что перемещение может быть отменено, но не повезло: 1376. static_cast для временного обращения к значению rvalue Спасибо @dyp за решение этой проблемы.
Я думаю, что это просто Оптимизация возвращаемого значения. Вы создаете копию в функциях на X tx(src);
, а затем эта локальная переменная просто возвращается к основной. Семантически как копия, но на самом деле операция копирования опущена.
Как уже говорили другие, ходы также могут быть опущены.
Согласно стандарту § 12.8 [ Копирование и перемещение объектов класса ]
31
Когда определенные критерии выполнены, реализация может опустить конструкцию копирования / перемещения объекта класса, даже если конструктор, выбранный для операции копирования / перемещения и / или деструктор для объекта, имеет побочные эффекты. В таких случаях реализация рассматривает источник и цель пропущенной операции копирования / перемещения как просто два разных способа обращения к одному и тому же объекту, и уничтожение этого объекта происходит в более поздние времена, когда два объекта были бы уничтожено без оптимизации.124 Это исключение операций копирования / перемещения, которое называется разрешением копирования, разрешено при следующих обстоятельствах (которые могут быть объединены для удаления нескольких копий)
в операторе возврата в функции с возвращаемым типом класса, когда выражение является именем энергонезависимого автоматического объекта (кроме параметра функции или предложения catch) с тем же типом cvunqualified, что и тип возврата функции, копия Операцию /move можно опустить, создав автоматический объект непосредственно в возвращаемое значение функции.
когда временный объект класса, который не был привязан к ссылке (12.2), будет скопирован / перемещен в объект класса с тем же типом cv-unqualified, операция копирования / перемещения может быть опущена путем создания временного объекта непосредственно в цель опущенной копии / перемещения
Таким образом, в обоих случаях (т.е. x41
, x51
соответственно) вы испытываете эффект оптимизации копирования.