Есть ли простой пример построения хода, который не будет исключен?

Я пытаюсь изучить семантику перемещения достаточно хорошо, чтобы представить ее своим ученикам. Я использовал очень упрощенные векторные или строковые классы, которые управляют памятью и члены которой выводят сообщения, чтобы продемонстрировать свою активность. Я пытаюсь разработать простой набор примеров, чтобы показать студентам.

Конструкция elision для RVO и в других местах в gcc 4.7 и clang активно устраняет конструкцию копирования и перемещения, поэтому, хотя я могу легко видеть назначение перемещения в работе, единственный раз, когда я видел конструкцию перемещения в работе, это если я отключаю конструкцию elision в gcc 4.7 с -fno-elide-конструкторами.

Явная копия конструкции оператора

MyString newString(oldString);

вызовет конструктор копирования, даже если elision включен. Но что-то вроде

MyString newString(oldString1 + oldString2);

не вызывает конструктор перемещения из-за исключения.

Все, что явно использует std :: move, не будет простым примером, потому что объяснение std :: move должно произойти позже.

Поэтому мой вопрос: существует ли простой пример кода, который будет вызывать конструкцию перемещения, даже если исключаются конструкторы копирования / перемещения?

7

Решение

Простым примером будет аргумент возвращаемой функции. Стандарт явно запрещает исключать движение в этом случае (не то, чтобы они могли …):

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. Если вам удастся написать код, который не попадает в эти категории, а тип имеет конструктор перемещения, будет вызван конструктор перемещения.

7

Другие решения

Хммм, посмотрим:

  • 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,

1