(constexpr
и noexcept
опущены, так как они кажутся неуместными для понимания поведения std::forward
.)
Основываясь на моем понимании «Эффективного современного C++» Скотта Мейерса, пример реализации std::move
в C++14 выглядит следующим образом.
template<typename T>
decltype(auto) move(T&& param) {
return static_cast<remove_reference_t<T>&&>(param);
}
Учитывая объяснение того, что такое переадресация (или «универсальная») ссылка, эта реализация, я думаю, довольно ясна для меня:
- параметр
param
имеет типT&&
, то есть ссылку на rvalue или ссылку на lvalue (в зависимости от типа аргумента), в зависимости от того, является ли аргумент rvalue или lvalue в вызывающей программе; другими словами,param
может привязываться как к rvalue, так и к lvalue (т. е. к чему угодно); так и задумано, посколькуmove
должно привести что угодно к rvalue. decltype(auto)
— это просто краткий способ выразить тип возвращаемого значения на основе фактического оператораreturn
.- возвращаемый объект является тем же самым объектом
param
, приведенным к ссылке rvalue (&&
) на любой типT
после того, как его выведенная ссылка удалена (вывод выполняется дляT&&
, а не для⋯<T>&&
).
Короче говоря, мое понимание использования пересылки/универсальных ссылок в реализации move
следующее:
- переадресация/универсальная ссылка
T&&
используется для параметра, поскольку он предназначен для привязки к чему-либо; - возвращаемый тип является ссылкой на rvalue, поскольку
move
предназначен для преобразования чего-либо в rvalue.
Было бы неплохо узнать, верно ли мое понимание до сих пор.
С другой стороны, пример реализации std::forward
в C++14 выглядит следующим образом.
template<typename T>
T&& forward(remove_reference_t<T>& param) {
return static_cast<T&&>(param);
}
Мое понимание следующее:
T&&
, тип возвращаемого значениядолжен быть пересылаемой/универсальной ссылкой, так как мы хотим, чтобыявляется ссылкой rvalue на любой аргумент типа шаблона, переданный вforward
возвращал либо ссылку rvalue, либо ссылку lvalue, следовательно, здесь происходит вывод типа возвращаемого типа (в отличие от того, что происходит дляmove
, где вывод типа происходит на стороне параметра)forward
;- поскольку
T
кодирует lvalue/rvalue-ness фактического аргумента, который связывает параметр вызывающего абонента, который передается в качестве аргумента дляforward
, самT
может иметь значениеactual_type&
илиactual_type
, следовательно,T&&
может быть либо ссылкой lvalue, либо ссылкой rvalue. - Тип
param
является ссылкой lvalue на типT
после того, как еговыведеннаяссылка удалена. На самом деле вstd::forward
вывод типа намеренно отключен, требуя, чтобы аргумент типа шаблона передавался явно.
Мои сомнения в следующем.
- Два экземпляра
forward
(по два для каждого типа, для которого он вызывается на самом деле) отличаются только типом возвращаемого значения (ссылка на rvalue при передаче rvalue, ссылка на lvalue при передаче lvalue), поскольку в обоих случаяхparam
имеет тип lvalue ссылка на не-const
без ссылокT
. Разве тип возвращаемого значения не учитывается при разрешении перегрузки? (Возможно, здесь я неправильно употребил слово «перегрузка».) - Поскольку тип
param
не являетсяconst
ссылкой lvalue наT
без ссылки, и поскольку ссылка lvalue должна быть to-const
для привязки к rvalue, какparam
может привязываться к rvalue?
В качестве побочного вопроса:
- можно ли использовать
decltype(auto)
для возвращаемого типа, как это делается дляmove
?
std::forward
являетсяforward(remove_reference_t<T>&& param)
, аremove_reference
не удаляет квалификаторы cv. - person Piotr Skotnicki   schedule 03.08.2019constexpr
иnoexcept
. - person Deduplicator   schedule 03.08.2019std::forward
? - person Enlico   schedule 03.08.2019