Почему в C ++ 11 есть неявные перемещения для параметров-значений, а не для параметров-значений? - PullRequest
20 голосов
/ 20 марта 2012

В C ++ 11 параметры значений (и другие значения) получают неявное перемещение при возврате:

A func(A a) {
    return a; // uses A::A(A&&) if it exists
}

По крайней мере в MSVC 2010 ссылочные параметры rvalue должны std::move:

A func(A && a) {
    return a; // uses A::A(A const&) even if A::A(A&&) exists
}

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

Какова мотивация относиться к ним по-разному в стандарте?

Ответы [ 2 ]

24 голосов
/ 20 марта 2012

Комитет по стандартизации приложил огромные усилия для создания формулировки, чтобы изменения могли произойти только при двух обстоятельствах:

  1. Когда ясно безопасно для этого.
  2. Когда пользователь явно просит (через std::move или аналогичное приведение).

Параметр значения, несомненно, будет уничтожен в конце функции. Поэтому возвращать его по ходу явно безопасно; он не может быть затронут другим кодом после возврата (если только вы не пытаетесь преднамеренно сломать что-то, и в этом случае вы, вероятно, вызвали неопределенное поведение). Поэтому его можно перенести из возврата.

Переменная && может указывать на временную переменную. Но это может быть ссылкой на lvalue (именованную переменную). Поэтому явно не 1019 * ясно безопасно двигаться от него; оригинальная переменная может скрываться. И поскольку вы не явно просили отойти от него (то есть: вы не вызывали std::move в этой функции), никакого движения не может быть.

Единственное время, когда переменная && будет неявно перемещена (т.е. без std::move), - это когда вы возвращаете это. std::move<T> возвращает T&&. Для этого возвращаемого значения допустимо вызывать конструктор перемещения, потому что это возвращаемое значение.

Теперь очень трудно назвать A func(A &&a) с lvalue без , вызывающим std::move (или эквивалентным приведением). Технически, это должно быть хорошо для параметров типа &&, которые будут неявно перемещены из. Но комитет по стандартизации хотел, чтобы ходы были явными для && типов, просто чтобы убедиться, что перемещение не произошло неявно в рамках этой функции. То есть он не может использовать знание вне функции о том, откуда взялся &&.

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

4 голосов
/ 20 марта 2012

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

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

...