Как использовать семантику перемещения с 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; }
Другие вопросы по тегам