В std::forward как он принимает rvalue?

Глядя на страницы 200-201 Effective Modern C++ Скотта Мейера, предлагаемой упрощенной реализацией std::forward может быть (правильная реализация была замечена в другом месте):

template <typename T>
T&& forward(std::remove_reference_t<T>& param)
{ return static_cast<T&&>(param); }

И при принятии виджета rvalue он становится:

Widget&& forward(Widget& param)
{ return static_cast<Widget&&>(param); }

Теперь, если вы возьмете этот замененный код и сделаете:

struct Widget { };

Widget&& forward(Widget& param)
{ return static_cast<Widget&&>(param); }

template <typename T>
void G(T&& uref)
{ }

template <typename T>
void F(T&& uref)
{ G(forward(uref)); }

int main()
{
  Widget x;
  F(std::move(x));
}

Что я не могу понять и еще не видел прямого ответа на SO, так это: в forward как параметру Widget& param удается принять Widget&& из F()? Обычно gcc-5.0 будет жаловаться так с кодом без шаблона:

ошибка: недопустимая инициализация неконстантной ссылки типа «Widget&» из rvalue типа «std::remove_reference::type {aka Widget}»

(Вопрос № 27501400 почти касается темы, но не совсем. Он показывает, что стандарт имеет оба lvalue & и rvalue && версии.)


person JasonM    schedule 13.03.2015    source источник
comment
Ссылки на именованные rvalue являются lvalue; реальные rvalue принимаются отдельной перегрузкой.   -  person T.C.    schedule 13.03.2015
comment
@Т.С. Это именно тот аспект, который я упустил из виду, спасибо.   -  person JasonM    schedule 13.03.2015
comment
Если у него есть имя, это lvalue. Итак, uref - это lvalue.   -  person Jonathan Wakely    schedule 14.03.2015
comment
@JasonM На самом деле я написал Скотту по электронной почте о той же проблеме, так как подумал, что это ошибка. По сути, он исключил перегрузку rvalue, так как не мог придумать для нее никакого варианта использования. Недавно я спросил об этом здесь   -  person vsoftco    schedule 26.04.2015


Ответы (1)


"Именованные ссылки rvalue являются lvalue",

поэтому пример отлично работает, как указано в комментарии.

Тем не менее, ваш код может быть изменен на

template <typename T>
void F(T && uref)
{
    G(forward(move(uref)));
}

который принимается другой перегрузкой (сравните):

template<typename T>
T && forward(typename std::remove_reference<T>::type & t)
{
    return static_cast<T &&>(t);
}


template<typename T>
T && forward(typename std::remove_reference<T>::type && t)
{
    static_assert(!std::is_lvalue_reference<T>::value, "T is an lvalue reference");
    return static_cast<T &&>(t);
}

Вторая перегрузка будет использоваться для rvalue. Это работает, если T равно Widget или Widget &&, а утверждение не выполняется для Widget &.

person Lukáš Bednařík    schedule 01.04.2015
comment
Зачем передавать rvalue в std::forward? - person 0x499602D2; 06.06.2015
comment
@ 0x499602D2 Вероятно, случайно. И если вы сделаете это, вы получите ошибку static_assert. - person Lukáš Bednařík; 29.09.2015