// std::remove_reference template <typename T> struct remove_reference { typedef T type; }; template <typename T> struct remove_reference<T&> { typedef T type; }; template <typename T> struct remove_reference<T&&> { typedef T type; }; // std::move template <typename T> typename remove_reference<T>::type&& move(T&& arg) noexcept { return static_cast<typename remove_reference<T>::type&&>(arg); } // std::forward template <typename T> T&& forward(typename remove_reference<T>::type& arg) noexcept { return static_cast<T&&>(arg); } template <typename T> T&& forward(typename remove_reference<T>::type&& arg) noexcept { return static_cast<T&&>(arg); }
Вот некоторые основные моменты:
remove_reference
— это шаблонная структура, предоставляющая нессылочный тип типа. Он имеет три частичные специализации шаблона для обработки ссылок lvalue (T&
), ссылок rvalue (T&&
) и типов, не являющихся ссылками (T
). Результат преобразования сохраняется в членеtype
.std::move
— это шаблон функции, который принимает универсальную ссылку (т. е.T&&
) в качестве аргумента. Аргумент преобразуется в ссылку rvalue с помощьюstatic_cast
после удаления ссылочного типа с помощьюremove_reference
. Цельюstd::move
является сигнал вызывающей стороне о том, что объект предназначен для перемещения, а не копирования.std::forward
— это шаблон функции, который приводит аргумент либо к ссылке lvalue, либо к типу ссылки rvalue, в зависимости от категории значения аргумента, с которым он вызывается. Использованиеremove_reference
в типе параметраforward
предназначено для получения базового нессылочного типа аргумента, чтобы можно было вывести правильный ссылочный тип и выполнить правильное приведение. Причина использованияstd::forward
состоит в том, чтобы обеспечить идеальную пересылку, которая позволяет передать аргумент в шаблон функции таким образом, чтобы его категория значения (т.е., является ли это lvalue или rvalue) сохранялась. Это полезно, например, в шаблонах функций, которые реализуют семантику перемещения, чтобы гарантировать, что аргументы перемещаются, а не копируются, когда они являются rvalue, и копируются, когда они являются lvalue.
Вот несколько тестов для проверки их функциональности:
void foo(int&) { std::cout << "lvalue reference" << std::endl; } void foo(int&&) { std::cout << "rvalue reference" << std::endl; } template <typename T> void bar(T&& t) { foo(forward<T>(t)); } int main() { int i = 0; std::cout << std::boolalpha << std::is_same<remove_reference<decltype((i))>::type, int>::value << std::endl; std::cout << std::boolalpha << std::is_same<remove_reference<decltype(std::move(i))>::type, int>::value << std::endl; foo(move(i)); bar(i); bar(move(i)); }
Результаты этого кода следующие:
- Первые два вызова
std::is_same
проверяют, правильно лиremove_reference
удаляет ссылку из своего аргумента. В первом вызовеremove_reference
применяется кi
, которое является lvalue типаint
, так что результатом являетсяint
, аstd::is_same
возвращаетtrue
. Во втором вызовеremove_reference
применяется кstd::move(i)
, которое является rvalue типаint&&
, поэтому результатом также являетсяint
, аstd::is_same
возвращаетtrue
. - Вызов
foo(std::move(i))
вызывает перегрузку ссылки rvaluefoo
, которая выводит «ссылку rvalue». - Первый вызов
bar(i)
передает lvalue типаint
шаблону функцииbar
.bar
затем вызываетforward<T>(t)
, гдеT
выводится какint&
, аt
является lvalue типаint&
.forward
приводитt
к ссылке lvalue типаint&
, поэтомуfoo(forward<T>(t))
вызывает перегрузку ссылки lvaluefoo
, которая печатает «ссылку lvalue». - Второй вызов
bar(std::move(i))
передает rvalue типаint&&
в шаблон функцииbar
.bar
затем вызываетforward<T>(t)
, гдеT
выводится какint&&
, аt
является rvalue типаint&&
.forward
приводитt
к ссылке rvalue типаint&&
, поэтомуfoo(forward<T>(t))
вызывает перегрузку ссылки rvaluefoo
, которая печатает «ссылку rvalue».
Таким образом, общий вывод программы:
true true rvalue reference lvalue reference rvalue reference