C ++ 11: Включается ли семантика перемещения для передачи по значению? - PullRequest
8 голосов
/ 15 марта 2012

У меня есть API, который выглядит следующим образом:

void WriteDefaultFileOutput(std::wostream &str, std::wstring target)
{
    //Some code that modifies target before printing it and such...
}

Интересно, было бы разумно включить семантику перемещения, выполнив это:

void WriteDefaultFileOutput(std::wostream &str, std::wstring&& target)
{
    //As above
}
void WriteDefaultFileOutput(std::wostream &str, std::wstring const& target)
{
    std::wstring tmp(target);
    WriteDefaultFileOutput(str, std::move(tmp));
}

или это просто шаблон, который компилятор должен быть в состоянии выяснить в любом случае?

Ответы [ 2 ]

16 голосов
/ 15 марта 2012

«Передать по значению» может означать либо копирование, либо перемещение;если аргумент является lvalue, вызывается конструктор копирования.Если аргумент является значением r, вызывается конструктор перемещения.Вам не нужно делать ничего особенного, включая ссылки на rvalue, чтобы получить семантику перемещения с передачей по значению.

Я углубляюсь в эту тему подробнее в Что такое семантика перемещения?

2 голосов
/ 15 марта 2012

Вы должны предпочесть передачу по значению, если вы собираетесь сделать копию с неподвижными значениями.Например, ваша const& версия WriteDefaultFileOutput явно копирует параметр, а затем перемещает копию с версией перемещения.Это означает, что если WriteDefaultFileOutput вызывается с подвижным значением (xvalue или prvalue), то оно будет перемещено, а если оно вызывается с lvalue, оно будет скопировано.

Это означает, что Нет Разница между двумя формами этой функции.Подумайте об этом:

WriteDefaultFileOutput(L"SomeString");

В вашем первом случае будет создан временный wstring.Временные значения являются значениями, поэтому они будут «перемещены» в параметр (поскольку wstring имеет конструктор перемещения).Конечно, любой компилятор, достойный его соли, исключит этот шаг и просто создаст временное значение непосредственно в параметре.

Во втором случае более или менее происходит то же самое.Временный wstring создан.Временные значения являются значениями, поэтому они могут связываться с && типами параметров.Поэтому он вызовет первую версию вашей функции со ссылкой на временное значение r.Единственное возможное отличие будет от компилятора, который не исключит ход.И даже тогда, перемещение не так дорого (в зависимости от вашей basic_string реализации).

Теперь рассмотрим это:

std::wstring myStr{L"SomeString"};
WriteDefaultFileOutput(myStr);

В первом случае вызов WriteDefaultFileOutput приведет к копированию значения myStr в параметр функции.

Во втором случае myStr является lvalue. не может привязаться к параметру &&.Поэтому единственная версия, которую он может вызвать, - это версия const&.Эта функция создаст копию вручную, а затем переместит копию вместе с другой.

Аналогичный эффект.Ваша первая версия имеет меньше кода, так что следуйте по понятным причинам.

В общем, я бы сказал, что есть только две причины для принятия параметра в качестве &&:

  1. Вы пишете конструктор перемещения.
  2. Вы пишете функцию пересылки и вам необходимо использовать идеальную пересылку.

Во всех других случаях, когда вы хотите, чтобы перемещение было возможным, простопринять значение.Если пользователь хочет скопировать, пусть копирует.Я полагаю, если вы хотите явно запретить копирование параметра, вы можете взять &&.Но главная проблема заключается в ясности.

Если вы берете параметр-значение, а пользователь предоставляет перемещаемое значение, то предоставленное пользователем значение будет всегда перемещаться.Например, ваша && версия WriteDefaultFileOutput не имеет для фактического перемещения данных из своего параметра.Это конечно может.Но это не обязательно.Если бы он принял значение, то он бы уже запросил данные.

Поэтому, если функция принимает параметр значения, и вы видите std::move в этом значении, то вы знаете объект, который был перемещен, теперь пуст. гарантировано было перемещено из.

...