Я пытаюсь изучить семантику перемещения достаточно хорошо, чтобы представить ее своим ученикам. Я использовал очень упрощенные векторные или строковые классы, которые управляют памятью и члены которой выводят сообщения, чтобы продемонстрировать свою активность. Я пытаюсь разработать простой набор примеров, чтобы показать студентам.
Конструкция elision для RVO и в других местах в gcc 4.7 и clang активно устраняет конструкцию копирования и перемещения, поэтому, хотя я могу легко видеть назначение перемещения в работе, единственный раз, когда я видел конструкцию перемещения в работе, это если я отключаю конструкцию elision в gcc 4.7 с -fno-elide-конструкторами.
Явная копия конструкции оператора
MyString newString(oldString);
вызовет конструктор копирования, даже если elision включен. Но что-то вроде
MyString newString(oldString1 + oldString2);
не вызывает конструктор перемещения из-за исключения.
Все, что явно использует std :: move, не будет простым примером, потому что объяснение std :: move должно произойти позже.
Поэтому мой вопрос: существует ли простой пример кода, который будет вызывать конструкцию перемещения, даже если исключаются конструкторы копирования / перемещения?
Простым примером будет аргумент возвращаемой функции. Стандарт явно запрещает исключать движение в этом случае (не то, чтобы они могли …):
std::vector<int> multiply( std::vector<int> input, int value ) {
for (auto& i : input )
i *= value;
return input;
}
Кроме того, вы можете явно запросить конструкцию перемещения для еще более простого, хотя и немного более искусственного примера:
T a;
T b( std::move(a) );
Хм … еще один, который не включает std::move
(технически это может быть исключено, но большинство компиляторов, вероятно, не будут):
std::vector<int> create( bool large ) {
std::vector<int> v1 = f();
std::vector<int> v2 = v1; // modify both v1 and v2 somehow
v2.resize( v2.size()/2 );
if ( large ) {
return v1;
} else {
return v2;
}
}
В то время как оптимизатор может исключить его, переписав код следующим образом:
std::vector<int> create( bool large ) {
if ( large ) {
std::vector<int> v1 = f();
std::vector<int> v2 = v1; // modify both v1 and v2 somehow
v2.resize( v2.size()/2 );
return v1;
} else {
std::vector<int> v1 = f();
std::vector<int> v2 = v1; // modify both v1 and v2 somehow
v2.resize( v2.size()/2 );
return v2;
}
}
Я почти сомневаюсь, что компилятор действительно сделает это. Обратите внимание, что в каждом пути кода объект, возвращаемый в известном ранее v1
а также v2
создаются, поэтому оптимизатор может найти нужный объект в месте возврата после перезаписи.
Ситуации, когда копирование / перемещение могут быть исключены, описаны в 12.8 / 31. Если вам удастся написать код, который не попадает в эти категории, а тип имеет конструктор перемещения, будет вызван конструктор перемещения.
Хммм, посмотрим:
MyString newString(oldString)
является копия. Здесь нечего делать; мы действительно в конечном итоге два объекты.
MyString newString(oldString1 + oldString2);
копии из временного, так что копия может быть исключена, и конкатенация строится непосредственно на месте.
Вот действительно ужасно дешевый пример строительства недопустимого хода:
MyString boo()
{
MyString s("Hello");
return std::move(s); // move-construction from the local "s", never elided
}
Явное приведение делает s
не подходит для RVO, поэтому возвращаемое значение будет построено из s
,