Как использовать семантику перемещения с std::string во время возврата функции?
Возможный дубликат:
C++ 11 значения и путаница в семантике перемещения
То, что я считаю правильным,
std::string GetLine()
{
std::string str;
std::getline(std::cin, str);
return std::move(str);
}
Но по этой ссылке http://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html(проверьте часть заголовка. Возврат явной ссылки на rvalue из функция)
который является хитом поиска Google #1 для семантики перемещения, показывает аналогичную сигнатуру функции как
int&& GetInt()
{
int x = 0;
// code here
return std::move(x);
}
Из того, что я читаю в других местах, && означает ссылку на rvalue, поэтому в этом случае она возвращает ссылку на объект, который не существует.
Так что это?
(Да, я знаю, что перемещение int не имеет реальной выгоды, но вопрос в том, использовать ли возвращаемый тип std::string или std::string&& в первой функции. И если это так, как это должно быть сделано для всех типов.)
3 ответа
Вы абсолютно правы в том, что int&& GetInt()
пример неверен и возвращает ссылку на уничтоженный объект. Однако, если я не пропустил это, ссылка, которую вы разместили, на самом деле не показывает никакого кода, возвращающего ссылку на локальную переменную. Вместо этого я вижу ссылку на возвращаемую глобальную переменную, что нормально.
Вот как вы используете семантику перемещения при возврате:
std::string func()
{
std::string rv;
/* ... */
return rv;
}
Вы вообще не должны использовать std::move()
при возврате объекта. Причина этого в том, что перемещение уже неявно разрешено в любое время, когда может произойти RVO, и использование std::move()
будет подавлять РВО. Итак, используя std::move()
никогда не будет лучше и часто будет хуже, чем просто возвращаться нормально.
Опять же, используя std::move()
может быть хуже, чем просто присвоить имя возвращаемой переменной, поскольку она подавляет оптимизацию возвращаемого значения. Оптимизация возвращаемого значения позволяет объекту быть возвращенным вызывающей стороне без необходимости копировать этот объект
в
return
оператор в функции с типом возвращаемого класса, когда выражение является именем энергонезависимого автоматического объекта (кроме параметра функции или предложения catch) с тем же типом cv-unqualified, что и тип возвращаемого функцией, copy/ Операция перемещения может быть опущена путем создания автоматического объекта непосредственно в возвращаемое значение функции- [class.copy] 12,8/31
Но используя std::move()
не позволяет возвращаемому выражению быть именем возвращаемого объекта. Вместо этого выражение является более сложным, и язык больше не имеет права обрабатывать его.
Причина, по которой имя объекта не хуже, чем использование std::move()
потому что есть другое правило, которое говорит, что выражение уже может рассматриваться как значение без необходимости std::move()
,
Когда критерии для исключения операции копирования выполнены или будут выполнены, за исключением того факта, что исходный объект является параметром функции, а копируемый объект обозначен lvalue, разрешением перегрузки для выбора конструктора для копирования является сначала выполняется, как если бы объект был обозначен как rvalue.
Отвечая на вопрос, вроде: вернуть string
, не move
что угодно, но лучше использовать (полагаться) RVO:
std::string func()
{
std::string rv;
/* ... */
return rv;
}
Вот как это обычно делается. Вы не можете вернуть (значение r или нет) ссылку на временный объект.
Нет необходимости говорить return std::move(str);
если str
является локальной переменной: если переменная удовлетворяет критериям для оптимизации возвращаемого значения, то в return
В заявлении переменная будет привязана к rvalue-ссылке.
Кроме того, помните, что вы, вероятно, не должны возвращать ссылку на локальную переменную, ни ссылку на lvalue, ни на rvalue.
В общем, вы должны иметь:
int foo() { int x; /*...*/ return x; }
std::string bar() { std::string str; /*...*/ return str; }