Возвращаются местные жители автоматически xvalues - PullRequest
18 голосов
/ 01 апреля 2012

Исходя из комментария, который я сделал к этому:

передача std :: vector в конструктор и перемещение семантики Требуется ли std::move в следующем коде, чтобы гарантировать, что возвращаемое значение является xvalue?

std::vector<string> buildVector()
{
  std::vector<string> local;

  // .... build a vector

  return std::move(local);
}

Насколько я понимаю, это необходимо. Я часто видел, как это использовалось при возврате std::unique_ptr из функции, однако GManNickG сделал следующий комментарий:

Насколько я понимаю, в операторе return все локальные переменные автоматически становятся значениями x (значения с истекающим сроком действия) и будут перемещены, но я не уверен, применимо ли это только к самому возвращаемому объекту. Так что OP должен пойти дальше и поставить это там, пока я не буду более уверен, что это не должно быть. :)

Может кто-нибудь уточнить, нужен ли std::move?

Зависит ли компилятор поведения?

Ответы [ 3 ]

15 голосов
/ 01 апреля 2012

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

Соответствующее примечание в 6.6.3 ["Оператор возврата"] (2):

Операция копирования или перемещения, связанная с оператором возврата, может быть исключена или рассматривается как значение с целью разрешения перегрузки при выборе конструктора (12.8).

Для пояснения это означает, что возвращаемый объект может быть создан с помощью перемещения из локального объекта.(хотя на практике RVO пропустит этот шаг полностью).Нормативной частью стандарта является 12.8 [«Копирование и перемещение объектов класса»] (31, 32), об исключении копий и значениях (спасибо @Mankarse!).


Вот глупый пример:

#include <utility>

struct Foo
{
    Foo()            = default;
    Foo(Foo const &) = delete;
    Foo(Foo &&)      = default;
};

Foo f(Foo & x)
{
    Foo y;

    // return x;         // error: use of deleted function ‘Foo::Foo(const Foo&)’
    return std::move(x); // OK
    return std::move(y); // OK
    return y;            // OK (!!)
}

Сравните это с возвратом фактического значения rvalue:

Foo && g()
{
    Foo y;
    // return y;         // error: cannot bind ‘Foo’ lvalue to ‘Foo&&’
    return std::move(y); // OK type-wise (but undefined behaviour, thanks @GMNG)
}
7 голосов
/ 01 апреля 2012

Хотя оба, return std::move(local) и return local, работают в том смысле, что они компилируются, их поведение отличается. И, вероятно, только последний был предназначен.

Если вы пишете функцию, которая возвращает std::vector<string>, вы должны вернуть std::vector<string> и именно его. std::move(local) имеет тип std::vector<string>&&, который не a std::vector<string>, поэтому его необходимо преобразовать в него с помощью конструктора перемещения.

Стандарт говорит в 6.6.3.2:

Значение выражения неявно преобразован в тип возврата функции, в которой он появляется.

Это означает, что return std::move(local) эквивалентно

std::vector<std::string> converted(std::move(local); // move constructor
return converted; // not yet a copy constructor call (which will be elided anyway)

, тогда как return local только

return local; // not yet a copy constructor call (which will be elided anyway)

Это избавляет вас от одной операции.


Чтобы дать вам краткий пример того, что это значит:

struct test {
  test() { std::cout << "  construct\n"; }
  test(const test&) { std::cout << "  copy\n"; }
  test(test&&) { std::cout << "  move\n"; }
};

test f1() { test t; return t; }
test f2() { test t; return std::move(t); }

int main()
{
  std::cout << "f1():\n"; test t1 = f1();
  std::cout << "f2():\n"; test t2 = f2();
}

Это выдаст

f1():
  construct
f2():
  construct
  move
4 голосов
/ 01 апреля 2012

Я думаю, что ответ - нет. Официально только примечание, §5 / 6 суммирует, что выражения являются / не являются xvalues:

Выражение является xvalue, если оно:

  • результат вызова функции, неявной или явной, чей тип возвращаемого значения является rvalue ссылкой на тип объекта,
  • приведение к rvalue-ссылке на тип объекта,
  • выражение доступа к члену класса, обозначающее нестатический член данных не ссылочного типа, в котором выражение объекта является xvalue, или
  • a. * Выражение указателя на член, в котором первый операнд является xvalue, а второй операнд - указатель на член данных.

Как правило, эффект этого правила заключается в том, что именованные ссылки на значения rvalue обрабатываются как lvalues, а безымянные ссылки на значения rvalue - как значения xvalue; rvalue ссылки на функции обрабатываются как lvalues ​​независимо от того, названы они или нет.

Кажется, здесь применяется первый пункт пули. Поскольку рассматриваемая функция возвращает значение, а не ссылку на rvalue, результат не будет xvalue.

...