Вот пример, возможно, архетипический, для которого нам нужно remove_reference
, в реализации std::move
: цель состоит в том, чтобы возвратить ссылочный тип rvalue, основанный на выведенном выведенном типе аргумент функции.
Пример: Foo x; move(x);
Здесь move(x)
должен возвращать тип Foo&&
. Но аргумент move
является выражением типа Foo&
. Так как же функция move
может определить правильный тип?
Первая попытка - использовать обычное вычитание аргумента шаблона и использовать приведение:
template <typename T> T && move(??? x) { return static_cast<T&&>(x); }
Но что должно войти в ???
? Если мы скажем T x
, то T
будет выведено как Foo&
; если мы говорим T & x
, то T = Foo
, и если мы говорим T && x
, это не будет совпадать вообще. Вторая версия, T & x
, представляется полезной.
Но тогда функция не будет работать с rvalues для начала (например, move(Foo(...))
. В этом случае нам нужно T && x
, чтобы T = Foo
и T&& = Foo&&
были желаемыми. Мы могли бы иметь две перегрузки, но наличие нескольких перегрузок нежелательно, поскольку без необходимости увеличивает сложность. И, наконец, если бы кто-то явно указывал параметр шаблона как move<Foo&>(x)
, функция никогда не работала бы, потому что когда T = Foo&
, то T&& = Foo&
а также.
Итак, приходит remove_reference
:
template <typename T>
typename std::remove_reference<T>::type && move(T && x)
{
return static_cast<typename std::remove_reference<T>::type &&>(x);
}
Прежде всего, новые правила свертывания ссылок подразумевают, что T
выводится как Foo&
или Foo&&
в обоих случаях. Затем remove_reference
удаляет ссылку и дает тип Foo
в любом случае, а добавление &&
делает желаемый Foo&&
тип возвращаемого значения.
В упрощенном изложении: нам нужно remove_reference
, потому что (Foo&)&&
равно Foo&
, а не Foo&&
. Если вы когда-нибудь напишите код шаблона, для которого требуется базовый тип параметра шаблона, который может быть выведен как U&
или U&&
, вы можете использовать эту модель.