Тема вроде уже заезжанная, но вот не могу уложить себе один момент в голове.
Скрытый текст
// TEMPLATE FUNCTION forwardtemplate<class _Ty> inline
_Ty&& forward(typename remove_reference<_Ty>::type& _Arg) // Зачем реализовано именно так?
{ // forward an lvaluereturn (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_referencetemplate<class _Ty>
struct remove_reference
{ // remove referencetypedef _Ty type;
};
template<class _Ty>
struct remove_reference<_Ty&>
{ // remove referencetypedef _Ty type;
};
template<class _Ty>
struct remove_reference<_Ty&&>
{ // remove rvalue referencetypedef _Ty type;
};
Казалось бы, почему бы не сделать так:
Скрытый текст
// TEMPLATE FUNCTION forwardtemplate<class _Ty> inline
_Ty&& forward(typename remove_reference<_Ty>::type_Ty& _Arg) // Почему не так?
{ // forward an lvaluereturn (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);
}
Здравствуйте, Abyx, Вы писали:
A>вообще начать надо с того, при типичном использовании forward, она всегда работает в lvalue, и вторая форма (c &&) не нужна. A>
A>template<typename T>
A>void test(T&& t) { use(forward<T>(t)); }
A>
Помню когда изучал forward, наличие этой второй формы сбивало с толку. Всё стало на свои места, когда понял что по сути используется только один вариант.
Здравствуйте, Engler, Вы писали:
E>Перечитал и пересмотрел кучу материала по теме RValue references
...
E>Тема вроде уже заезжанная, но вот не могу уложить себе один момент в голове.
If you want to dig a little deeper for extra credit, ask yourself this question: why is the remove_reference in the definition of std::forward needed? The answer is, it is not really needed at all. If you use just S& instead of remove_reference<S>::type& in the defintion of std::forward, you can repeat the case distinction above to convince yourself that perfect forwarding still works just fine. However, it works fine only as long as we explicitly specify Arg as the template argument of std::forward. The purpose of the remove_reference in the definition of std::forward is to force us to do so.
если коротко то общие правила такие:
1) в std:::move не следует указывать тип: std::move(var), а не std::move<T>(var)
2) в std::forward необходимо указать тип: std::forward<T>(var), а не std::forward(var)
если вы отошли от этого правила, то высока вероятность, что вы делаете что-то не так
U>If you want to dig a little deeper for extra credit, ask yourself this question: why is the remove_reference in the definition of std::forward needed? The answer is, it is not really needed at all. If you use just S& instead of remove_reference<S>::type& in the defintion of std::forward, you can repeat the case distinction above to convince yourself that perfect forwarding still works just fine. However, it works fine only as long as we explicitly specify Arg as the template argument of std::forward. The purpose of the remove_reference in the definition of std::forward is to force us to do so.
Я вчитывался в этот момент, ни один раз.Просто пробовал на старой версии gcc (4.8.1/2) тесты вели себя чуть-чуть по другому, поэтому и вгоняло в ступор.
Сейчас другой момент (gcc 4.9.0):
#include <iostream>
#include <utility>
class X{};
template<typename T>
void Test(T&) { std::cout<< 1;}
template<typename T>
void Test(T&&) {std::cout<< 2; }
template<typename T>
T&& myForward(T& arg)
{
return static_cast<T&&>(arg);
}
template<typename T>
void Func(T&& arg)
{
// Test(std::forward<T>(arg));
// Test(myForward<T>(arg)); Работают экквивалентно. Выводит 12
//
// В этом моменте вывод типов срабатывает немного по-другому. Почему?
//
Test(myForward(arg)); // Выводит 22
}
int main()
{
X x1;
// lvalue : Expected version: Test(T&)
Func(x1);
// rvalue : Expected version: Test(T&&)
Func(X());
}
Инетерсно с чем связано такое поведение?
U>2) в std::forward необходимо указать тип: std::forward<T>(var), а не std::forward(var) U>если вы отошли от этого правила, то высока вероятность, что вы делаете что-то не так
Ну вот так-то и не даст сделать.
Здравствуйте, Abyx, Вы писали:
A>Здравствуйте, Engler, Вы писали:
A>вообще начать надо с того, при типичном использовании forward, она всегда работает в lvalue, и вторая форма (c &&) не нужна.
Почему ее тогда в библиотеке внесли (или оставили)? в С++ 14?
Здравствуйте, Engler, Вы писали:
A>>вообще начать надо с того, при типичном использовании forward, она всегда работает в lvalue, и вторая форма (c &&) не нужна. E>Почему ее тогда в библиотеке внесли (или оставили)? в С++ 14?
U>2) в std::forward необходимо указать тип: std::forward<T>(var), а не std::forward(var)
Кстати, offtopic, тип T для forward можно извлечь из var, что позволяет спрятать всё в один макрос FORWARD(var) (по-моему прочитал эту идею в комментариях к статье Майерса):
EP>Кстати, offtopic, тип T для forward можно извлечь из var, что позволяет спрятать всё в один макрос FORWARD(var) (по-моему прочитал эту идею в комментариях к статье Майерса): EP>