Стандартная подпись std::forward
:
template<typename T>
constexpr T&& forward(std::remove_reference_t<T>&) noexcept;
template<typename T>
constexpr T&& forward(std::remove_reference_t<T>&&) noexcept;
Поскольку тип параметра не T
напрямую, мы должны указать аргумент шаблона при использовании std::forward
:
template<typename... Args>
void foo(Args&&... args)
{
bar(std::forward<Args>(args)...);
}
Однако иногда аргумент шаблона не так прост, как Args
. auto&&
- это случай:
auto&& vec = foo();
bar(std::forward<decltype(vec)>(vec));
Вы также можете представить более сложные типы аргументов шаблона для std::forward
. В любом случае, интуитивно std::forward
должен знать, что такое T
, но на самом деле это не так.
Так что моя идея состоит в том, чтобы опустить <Args>
и <decltype(vec)>
независимо от того, насколько они просты. Вот моя реализация:
#include <type_traits>
template<typename T>
std::add_rvalue_reference_t<std::enable_if_t<!std::is_lvalue_reference<T>::value, T>>
my_forward(T&& obj)
{
return std::move(obj);
}
template<typename T>
T& my_forward(T& obj)
{
return obj;
}
int main()
{
my_forward(1); // my_forward(int&&)
int i = 2;
my_forward(i); // my_forward(int&)
const int j = 3;
my_forward(j); // my_forward(const int&)
}
Когда obj
является rvalue ссылкой, например int&&
, выбрана первая перегрузка, потому что T
равен int
, чей is_lvalue_reference
равен false;
Когда obj
является ссылкой на lvalue, например const int&
, выбирается вторая перегрузка, потому что T
равен const int&
, а первая отключена SFINAE.
Если моя реализация возможно, почему std::forward
все еще требует <T>
? (Так что мое должно быть неосуществимо.)
Если нет, то что не так? И все же вопрос, можно ли пропустить параметр шаблона в std::forward
?