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

В 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 ответа

Решение

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

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

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

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

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

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

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

Это было исправлено для C++20 в P0527 и P1825. Единственный способ привязать параметр функции к ссылке rvalue состоит в том, чтобы источник был либо временным, либо вызывающий объект явно приводил невременное значение к rvalue (например, сstd::move). Таким образом, эта "обязательная оптимизация" была признана безопасной.

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

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

Другие вопросы по тегам