std::forward, зачем typename remove_reference<_Ty>::type?
От: Engler Беларусь  
Дата: 10.07.14 18:10
Оценка:
Всем привет,

Перечитал и пересмотрел кучу материала по теме RValue references
  Скрытый текст
http://www.rsdn.ru/forum/cpp/3731204.1
Автор: Masterkent
Дата: 11.03.10

http://thbecker.net/articles/rvalue_references/section_01.html
http://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers
http://stackoverflow.com/questions/7779900/why-is-template-argument-deduction-disabled-with-stdforward
http://channel9.msdn.com/Events/GoingNative/2013/An-Effective-Cpp11-14-Sampler

Тема вроде уже заезжанная, но вот не могу уложить себе один момент в голове.

  Скрытый текст
    // TEMPLATE FUNCTION forward
template<class _Ty> inline
    _Ty&& forward(typename remove_reference<_Ty>::type& _Arg) // Зачем реализовано именно так?
    {    // forward an lvalue
    return (static_cast<_Ty&&>(_Arg));
    }

template<class _Ty> inline
    _Ty&& forward(typename remove_reference<_Ty>::type&& _Arg) _NOEXCEPT // Зачем реализовано именно так?
    {    // forward anything
    static_assert(!is_lvalue_reference<_Ty>::value, "bad forward call");
    return (static_cast<_Ty&&>(_Arg));
    }

// Здесь со специализациями шаблона, понятно как это работает. Но вот непонятно почему это реализовано именно так?
    // TEMPLATE remove_reference
template<class _Ty>
    struct remove_reference
    {    // remove reference
    typedef _Ty type;
    };

template<class _Ty>
    struct remove_reference<_Ty&>
    {    // remove reference
    typedef _Ty type;
    };

template<class _Ty>
    struct remove_reference<_Ty&&>
    {    // remove rvalue reference
    typedef _Ty type;
    };

Казалось бы, почему бы не сделать так:
  Скрытый текст
    // TEMPLATE FUNCTION forward
template<class _Ty> inline
    _Ty&& forward(typename remove_reference<_Ty>::type _Ty& _Arg) // Почему не так?
    {    // forward an lvalue
    return (static_cast<_Ty&&>(_Arg));
    }

template<class _Ty> inline
    _Ty&& forward(typename remove_reference<_Ty>::type _Ty&& _Arg) _NOEXCEPT // Почему не так?
    {    // forward anything
    static_assert(!is_lvalue_reference<_Ty>::value, "bad forward call");
    return (static_cast<_Ty&&>(_Arg));
    }


А дальше начинается самое интересное.
При таком коде компилятор (от VS 2012 и g++ 4.8.2)
  Скрытый текст
ПРИМЕР1 :
template<typename T>
void Test(T&) {}

template<typename T>
void Test(T&&) {}

int main()
{
    X x1;
    Test(x1);
}


Это от MS compiler
error C2668: 'Test' : ambiguous call to overloaded function
1> f:\projects\c++\stl_tests\main.cpp(94): could be 'void Test<X&>(T)'
1> with
1> [
1> T=X &
1> ]
1> f:\projects\c++\stl_tests\main.cpp(91): or 'void Test<X>(T &)'
1> with
1> [
1> T=X
1> ]
1> while trying to match the argument list '(X)'



Да, я знаком с "reference collapsing rules". Т.е почему так происходит я понимаю, но я не совсем понимаю, почему не выбирается явная реализация:

  Скрытый текст
template<typename T>
void Test(T&) {}


Почему так должно быть? Ну например, при перегрузке функций с явными типами все работает логично ( т.е выбирвается TestInt(int&) , т.к параметр x1 lvalue ) :

  Скрытый текст
void TestInt(int &) {}
void TestInt(int &&) {}

int main()
{

    int x1;
    TestInt(x1);
}


Небольшой поиск наталкивает:
На следующую тему: Overload ambiguity when passing R-value to function that takes L-value
Оттуда есть ссылка сюда: 1164. Partial ordering of f(T&amp;) and f(T&amp;&amp;)

В драфтовой версии С++ (n3793) в 14.8.2.4/9 Изменения внесли.

А, вот теперь вопросы:

1. Поведение компилятовов на код из "ПРИМЕР1", получается не соответствуют стандарту?
2. Именно по этому приходится извращаться с
typename remove_reference<_Ty>::type
в параметрах?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.