
// 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