Я просто обнаружил, что не до конца понимаю логику std::move()
.
Сначала я погуглил, но похоже, что есть только документы о том, как использовать std::move()
, а не о том , как работает его структура.
Я имею в виду, что я знаю, что такое функция-член шаблона, но когда я смотрю на std::move()
определение в VS2010, это все еще сбивает с толку.
определение std :: move () приведено ниже.
template<class _Ty> inline
typename tr1::_Remove_reference<_Ty>::_Type&&
move(_Ty&& _Arg)
{ // forward _Arg as movable
return ((typename tr1::_Remove_reference<_Ty>::_Type&&)_Arg);
}
В первую очередь мне кажется странным параметр (_Ty && _Arg), потому что, когда я вызываю функцию, как вы видите ниже,
// main()
Object obj1;
Object obj2 = std::move(obj1);
это в основном равно
// std::move()
_Ty&& _Arg = Obj1;
Но, как вы уже знаете, вы не можете напрямую связать LValue со ссылкой на RValue, что заставляет меня думать, что это должно быть так.
_Ty&& _Arg = (Object&&)obj1;
Однако это абсурд, потому что std :: move () должен работать для всех значений.
Итак, я думаю, чтобы полностью понять, как это работает, я должен также взглянуть на эти структуры.
template<class _Ty>
struct _Remove_reference
{ // remove reference
typedef _Ty _Type;
};
template<class _Ty>
struct _Remove_reference<_Ty&>
{ // remove reference
typedef _Ty _Type;
};
template<class _Ty>
struct _Remove_reference<_Ty&&>
{ // remove rvalue reference
typedef _Ty _Type;
};
К сожалению, это все еще сбивает с толку, и я этого не понимаю.
Я знаю, что это все из-за отсутствия у меня базовых навыков синтаксиса в C ++. Я хотел бы знать, как они работают, и любые документы, которые я могу найти в Интернете, будут более чем приветствоваться. (Если вы можете просто объяснить это, это тоже будет круто).
источник
move
работает, а не как это реализовано. Я считаю это объяснение действительно полезным: pagefault.blog/2018/03/01/… .Ответы:
Начнем с функции перемещения (которую я немного очистил):
Начнем с более простой части - то есть, когда функция вызывается с rvalue:
и наш
move
шаблон создается следующим образом:Поскольку
remove_reference
конвертируетT&
вT
илиT&&
вT
иObject
не является ссылкой, наша последняя функция:Теперь вы можете спросить: а нужен ли нам гипс? Ответ: да, есть. Причина проста; названный справочник Rvalue будет рассматриваться как именующие (и неявное преобразование из именующих в RValue ссылки запрещено стандарт).
Вот что происходит, когда мы вызываем
move
с lvalue:и соответствующий
move
экземпляр:Опять
remove_reference
конвертируемObject&
вObject
и получаем:Теперь мы переходим к сложной части: что вообще
Object& &&
означает и как оно может быть привязано к lvalue?Чтобы обеспечить идеальную пересылку, стандарт C ++ 11 предоставляет специальные правила для сворачивания ссылок, а именно:
Как видите, под этими правилами на
Object& &&
самом деле подразумеваетсяObject&
простая ссылка lvalue, которая позволяет связывать lvalue.Таким образом, конечная функция:
что мало чем отличается от предыдущего экземпляра с rvalue - они оба приводят свой аргумент к ссылке rvalue, а затем возвращают ее. Разница в том, что первый экземпляр можно использовать только с rvalues, а второй работает с lvalues.
Чтобы объяснить, почему нам нужно
remove_reference
немного больше, давайте попробуем эту функциюи создайте его с помощью lvalue.
Применяя упомянутые выше правила сворачивания ссылок, вы можете увидеть, что мы получаем функцию, которую нельзя использовать как
move
(проще говоря, вы вызываете ее с lvalue, вы получаете lvalue обратно). Во всяком случае, эта функция является функцией идентичности.источник
T
asObject&
, я не знал, что это действительно сделано. Я ожидал,T
чтоObject
в этом случае также оценю , так как я думал, что это было причиной введения ссылок на оболочки иstd::ref
, или нет.template <typename T> void f(T arg)
(это и есть статья в Википедии) иtemplate <typename T> void f(T& arg)
. Первый разрешает значение (и если вы хотите передать ссылку, вы должны обернуть ееstd::ref
), а второй всегда разрешает ссылку. К сожалению, правила для шаблона аргумента дедукции являются довольно сложными, поэтому я не могу предоставить точные рассуждения , почемуT&&
resovles кObject& &&
(но это происходит на самом деле).template <typename T> T&& also_wanna_be_move(T& arg) { return static_cast<T&&>(arg); }
std::move
преобразовать только lvalues в rvalues, тогда да,T&
все будет в порядке. Этот трюк делается в основном для гибкости: вы можете вызватьstd::move
все (включая rvalue) и получить обратно rvalue._Ty - это параметр шаблона, и в этой ситуации
_Ty - это тип "Object &"
поэтому необходима ссылка _Remove_reference.
Было бы больше похоже
Если бы мы не удалили ссылку, это было бы так, как если бы мы делали
Но ObjectRef && сводится к Object &, который мы не могли привязать к obj2.
Причина, по которой он сокращается таким образом, заключается в поддержке идеальной пересылки. См. Эту статью .
источник
_Remove_reference_
это необходимо. Например, если у вас естьObject&
typedef и вы берете на него ссылку, вы все равно получитеObject&
. Почему это не работает с &&? На это есть ответ, и он связан с безупречной пересылкой.