2012-04-23 18 views
12

Przygotowałem walizkę testową, aby dowiedzieć się o perfekcyjnym przekazywaniu.C++ 11: subtelność std :: forward: Czy tożsamość jest naprawdę potrzebna?

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 działa zgodnie z oczekiwaniami. Interesującą zachowanie pojawia się, gdy zaimplementować własną funkcję naprzód bez tożsamości:

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

Wymiana std::forward z MyForward w zewnętrznej daje dokładnie ten sam wynik! Zachowanie nasuwa pytanie, dlaczego tożsamość jest używana?

Compiler VS2010

Aktualizacja 1: W odniesieniu do zapobiegania TYP odliczenia

AFAIK, przepis szczególny rodzaj odliczenie jest aktywna tylko na T & &. Zwróć uwagę na definicję forward, forward(typename identity<T>::type& t). Typ argumentu ma tylko jedną wartość: &. W rzeczywistości, po zmianie funkcji MyForward na tożsamość i pominięciu rzutowania (T & &), przykład nie zostanie skompilowany. Na powierzchni rzut od l-wartości do rwartości wydaje się sprawiać, że praca naprzód.

Aktualizacja 2: testowane na ideone.com z GCC 4.5, to samo zachowanie.

+1

Co masz na myśli przez "tożsamość"? Dwa przeciążenia typu 'std :: forward' lub' remove_reference :: type'? – kennytm

+1

Tożsamość określa strukturę tożsamości, typ argumentu forward: _Ty && forward (tożsamość typu <_Ty> :: type & _Arg) –

+2

To musi być jakiś standardowy podpis. Standardowym jest 'T && forward (typename remove_reference :: type & t)' (i przeciążenie przyjmujące '&&'). – kennytm

Odpowiedz

13

remove_reference<T> (identity było w starej wersji projektu, ale został zmieniony na remove_reference) jest stosowany w celu zapobiegania typu odliczenie: std::forwardtylko współpracuje z wyraźną parametru typu. W przeciwnym razie skompilowałoby się:

std::forward(t) 

... ale nie zrobiłoby to dobrze.

Odnośnie problemu z wartościami l/rwal, proszę zauważyć, że istnieje two overloads of std::forward: jedna dla wartości l, druga dla wartości r.

W rzeczywistości, realizacja MyForward podana jest bardziej jak std::move: okazuje lwartościami do rvalues ​​(różnica jest taka, że ​​ruch akceptuje rvalues ​​również).

+0

patrz aktualizacja. –

+0

Jest tylko jeden ten VS2010. I działa z jednym. dlaczego potrzebujemy dwóch? –

+0

@CandyChiu działa na VS2010, ponieważ został dostarczony przed sfinalizowaniem standardu. Zasady zmieniły się między czasem wysłania VS2010 i opublikowaniem standardu. Potrzebujemy dwóch, ponieważ wartości l nie będą wiązały się z '&&', a wartości r nie będą wiązały się z '&'. –

0

sprawdziłem definicję forward i identity w VS 2010. Jedyna różnica między MyForward i ich forward jest użyć parametru T& i używają parametru typename identity<T>::type&. I identity<T>::type to tylko T.

Najważniejszym (być może jedynym) efektem tej różnicy jest to, że aby użyć ich forward, argument szablonu musi być jawnie określony, podczas gdy twój argument szablonu MyForward może zostać wywnioskowany z połączenia.

+0

"można wywnioskować" ... cóż, zostanie to błędnie wyprowadzone –

+0

Tak, jeśli przekazać lwartość na zewnętrzną (T &&), t będzie rzutowane na typ odniesienia rwartości. – Cosyn

Powiązane problemy