Подтвердить что ты не робот

С++ 11: тонкость std:: forward: действительно ли идентичность?

Я установил тестовый пример, чтобы узнать о идеальной пересылке.

std::string inner(const std::string& str ) {
return "const std::string&";
}
std::string inner(std::string& str ) {
    return "std::string&";
}
std::string inner(const std::string&& str ) {
    return "const std::string&&";
}
std::string inner(std::string&& str ) {
    return "std::string&&";
}

template <typename T> void outer(T&& t) {
  std::cout << "t: " << t << std::endl;
  std::cout << "perfect forward: " << inner(std::forward<T>(t)) << std::endl;
  std::cout << std::endl;
}

void PerfectForwarding()
{
     outer("literal");
     outer(lvalue);
     outer(constlvalue);
     outer(rvalue());
     outer(constrvalue());
}

std::forward работает так, как ожидалось. Интересное поведение возникает, когда я реализую свою собственную функцию прямого доступа без идентификации:

template <typename T> T&& MyForward(T& t)
{
   return ((T&&)t);
}

Замена std::forward на MyForward во внешнем дает тот же результат! Поведение вызывает вопрос, почему используется идентификатор?

Компилятор VS2010

Обновление 1: в отношении предотвращения вычитания типа

AFAIK, правило вывода специального типа активируется только на T & &. Обратите внимание на определение forward, forward(typename identity<T>::type& t). Тип аргумента имеет только один &. Фактически, после того, как я изменил MyForward на использование идентификатора и не использовал кастинг (T & &), этот пример не компилируется. На первый взгляд, отливка от lvalue до rvalue, похоже, делает передовую работу.

Обновление 2: проверено на ideone.com с GCC 4.5, такое же поведение.

4b9b3361

Ответ 1

remove_reference<T> (identity был в старой версии черновика, но был изменен на remove_reference) используется для предотвращения вывода типа: std::forward работает только с явным параметром типа. В противном случае следующее:

std::forward(t)

... но это не пойдет правильно.

Что касается проблемы с lvalues ​​/rvalues, пожалуйста, обратите внимание, что есть две перегрузки std::forward: одна для lvalues, другая для rvalues.

Фактически данная реализация MyForward больше похожа на std::move: она меняет значения lvalues ​​на rvalues ​​(разница в том, что move accepts rvalues).

Ответ 2

Я проверил определение forward и identity в VS 2010. Единственное различие между вашим MyForward и их forward заключается в том, что вы используете параметр T&, и они используют параметр typename identity<T>::type&. И identity<T>::type просто T.

Самый важный (возможно, только) эффект этой разницы заключается в том, что для использования их forward аргумент шаблона должен быть явно указан, в то время как ваш аргумент шаблона MyForward может быть выведен из вызова.