Как использовать семантику перемещения с помощью std::string во время возврата функции?

Возможный дубликат:
С++ 11 rvalues ​​и смещение семантики семантики

То, что я считаю правильным,

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-reference из функции)

который является # 1 поисковым ударом Google для семантики перемещения, показывает аналогичную сигнатуру функции

int&& GetInt()
{
int x = 0;
// code here
return std::move(x);
}

Из того, что я читал в других местах && означает rvalue reference, поэтому в этом случае возвращается ссылка на объект, который не существует.

Итак, что это?

(Да, я знаю, что перемещение int не имеет реальной выгоды, но вопрос заключается в том, следует ли использовать возвращаемый тип std::string или std::string && & && & в первой функции. И если это так, для всех типов.)

Ответы

Ответ 1

Вы абсолютно правы, что пример int&& GetInt() неверен и возвращает ссылку на уничтоженный объект. Однако, если я не пропустил его, ссылка, которую вы опубликовали, на самом деле не показывает код, возвращающий ссылку на локальную переменную. Вместо этого я вижу ссылку на возвращаемую глобальную переменную, которая в порядке.

Вот как вы используете семантику перемещения при возврате:

std::string func()
{
    std::string rv;
    /* ... */
    return rv;
}

Обычно вы не должны использовать std::move() при возврате объекта. Причиной этого является то, что перемещение уже неявно разрешено в любое время RVO, и использование std::move() будет подавлять RVO. Поэтому использование std::move() никогда не будет лучше и часто будет хуже, чем просто нормально вернуться.


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

в выражении return в функции с возвращаемым типом класса, когда выражение - это имя энергонезависимого автоматического объекта (другое чем параметр функции или catch-clause) с тем же cv-unqualified type как возвращаемый тип функции, копирование/перемещение операция может быть опущена путем непосредственного конструирования автоматического объекта в возвращаемое значение функции

- [class.copy] 12.8/31

Но использование std::move() не позволяет возвращенному выражению быть именем возвращаемого объекта. Вместо этого выражение более сложное, и языку больше не разрешается придавать ему особую управляемость.

Причина просто именования объекта не хуже, чем использование std::move(), потому что существует другое правило, в котором выражение, которое говорит, выражение уже может рассматриваться как rvalue без необходимости std::move().

Когда критерии для исключения операции копирования выполняются или будут сэкономленные за тот факт, что исходный объект является параметром функции, и подлежащий копированию объект обозначается значением lvalue, перегрузкой разрешение для выбора конструктора для копии сначала выполняется как будто объект был обозначен rvalue.

Ответ 2

Отвечая на вопрос, выполните следующие действия: return a string. Не move ничего, но используйте (полагайтесь на) RVO:

std::string func()
{
    std::string rv;
    /* ... */
    return rv;
}

Вот как это вообще должно быть сделано. Вы не можете вернуть ссылку (r-значение или нет) на временную.

Ответ 3

Нет необходимости говорить return std::move(str);, если str - локальная переменная: если переменная удовлетворяет критериям оптимизации возвращаемого значения, то в выражении return переменная будет привязана к ссылке rvalue.

Кроме того, остерегайтесь, что вы, вероятно, не должны возвращать ссылку на локальную переменную, ни ссылку lvalue, ни rvalue.

Все сказали, что у вас должно быть:

int foo() { int x; /*...*/ return x; }

std::string bar() { std::string str; /*...*/ return str; }