Первое: где определены std :: move и std :: forward?
См. 20.3
Служебные компоненты, <utility>
.
При реализации семантики перемещения источник предположительно остается в неопределенном состоянии. Должно ли это состояние быть действительным для объекта?
Очевидно, что объект все еще должен быть разрушаемым. Но, кроме того, я думаю, что это хорошая идея - быть назначаемым. Стандарт говорит для объектов, которые удовлетворяют "MoveConstructible" и "MoveAssignable":
[Примечание: rv остается действительным объектом. Его состояние не указано. - конец примечания]
Это будет означать, я думаю, что объект все еще может участвовать в любой операции, которая не устанавливает никаких предварительных условий. Это включает в себя CopyConstructible, CopyAssignable, Destructible и другие вещи. Обратите внимание, что это не потребует ничего для ваших собственных объектов с точки зрения основного языка. Требования вступают в силу только после прикосновения к стандартным компонентам библиотеки, в которых указаны эти требования.
Далее: если вас не волнует семантика перемещения, существуют ли какие-либо ограничения, которые могут привести к тому, что неконстантная ссылка будет предпочтительнее ссылки на rvalue при работе с параметрами функции?
Это, к сожалению, принципиально зависит от того, находится ли параметр в шаблоне функции и использует ли параметр шаблона:
void f(int const&); // takes all lvalues and const rvalues
void f(int&&); // can only accept nonconst rvalues
Однако для шаблона функции
template<typename T> void f(T const&);
template<typename T> void f(T&&);
Вы не можете этого сказать, потому что второй шаблон, после вызова с lvalue, будет иметь в качестве параметра синтезированного объявления тип U&
для неконстантных l-значений (и будет лучшим соответствием) и U const&
для постоянных значений (и быть неоднозначным). Насколько мне известно, не существует правила частичного упорядочения для устранения этой второй неоднозначности. Однако этот уже известен .
--
Редактировать --
Несмотря на этот отчет о проблеме, я не думаю, что эти два шаблона неоднозначны. Частичное упорядочение сделает первый шаблон более специализированным, потому что после удаления модификаторов ссылок и const
мы обнаружим, что оба типа одинаковы, а затем заметим, что первый шаблон имел ссылку на const. Стандарт гласит (14.9.2.4
)
Если для данного типа дедукция успешна в обоих направлениях (т. Е. Типы идентичны после приведенных выше преобразований) и если тип из шаблона аргумента является более квалифицированным по cv, чем тип из шаблона параметра ( как описано выше), этот тип считается более специализированным, чем другой.
Если для каждого рассматриваемого типа данный шаблон по крайней мере так же специализирован для всех типов и более специализирован для некоторого набора типов, а другой шаблон не более специализирован для каких-либо типов или по крайней мере не так специализирован для любых типов, тогда данный шаблон более специализирован, чем другой шаблон.
Это делает шаблон T const&
победителем частичного заказа (и GCC действительно правильно его выбрал).
--
Редактировать Конец --
Что подводит меня к моему последнему вопросу. Вы все еще не можете привязать временные ссылки к неконстантным ссылкам. Но вы можете связать их с неконстантными ссылками. * * 1068
Это хорошо объясняется в этой статье . Второй вызов с использованием function2
принимает только неконстантные значения. Остальная часть программы не заметит, если они будут изменены, потому что они больше не смогут получить доступ к этим значениям! И передаваемый вами 5
не является типом класса, поэтому создается скрытый временный объект, а затем передается в ссылку int&&
rvalue. Код, вызывающий function2
, не сможет получить доступ к этому скрытому объекту, поэтому не заметит никаких изменений.
Другая ситуация, если вы сделаете это:
SomeComplexObject o;
function2(move(o));
Вы явно запросили, чтобы o
было перемещено, поэтому оно будет изменено в соответствии с его спецификацией перемещения. Однако перемещение является логически неизменяющей операцией (см. Статью). Это означает, что если вы переезжаете или не должны быть заметны из вызывающего кода:
SomeComplexObject o;
moveit(o); // #1
o = foo;
Если вы сотрете движущуюся линию, поведение останется прежним, так как оно все равно перезаписывается. Это, однако, означает, что код, который использует значение o
после его перемещения, является bad , поскольку он нарушает этот неявный контракт между moveit
и вызывающим кодом. Таким образом, в стандарте не указывается конкретная стоимость перемещаемого контейнера.