// 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);
}

Вот некоторые основные моменты:

  1. remove_reference — это шаблонная структура, предоставляющая нессылочный тип типа. Он имеет три частичные специализации шаблона для обработки ссылок lvalue (T&), ссылок rvalue (T&&) и типов, не являющихся ссылками (T). Результат преобразования сохраняется в члене type.
  2. std::move — это шаблон функции, который принимает универсальную ссылку (т. е. T&&) в качестве аргумента. Аргумент преобразуется в ссылку rvalue с помощью static_cast после удаления ссылочного типа с помощью remove_reference. Целью std::move является сигнал вызывающей стороне о том, что объект предназначен для перемещения, а не копирования.
  3. 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)); 
}

Результаты этого кода следующие:

  1. Первые два вызова 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.
  2. Вызов foo(std::move(i)) вызывает перегрузку ссылки rvalue foo, которая выводит «ссылку rvalue».
  3. Первый вызов bar(i) передает lvalue типа int шаблону функции bar. bar затем вызывает forward<T>(t), где T выводится как int&, а t является lvalue типа int&. forward приводит t к ссылке lvalue типа int&, поэтому foo(forward<T>(t)) вызывает перегрузку ссылки lvalue foo, которая печатает «ссылку lvalue».
  4. Второй вызов bar(std::move(i)) передает rvalue типа int&& в шаблон функции bar. bar затем вызывает forward<T>(t), где T выводится как int&&, а t является rvalue типа int&&. forward приводит t к ссылке rvalue типа int&&, поэтому foo(forward<T>(t)) вызывает перегрузку ссылки rvalue foo, которая печатает «ссылку rvalue».

Таким образом, общий вывод программы:

true
true
rvalue reference
lvalue reference
rvalue reference