2013-04-24 17 views
5

Sto leggendo di inoltro perfetta, e questo è qualcosa che ho imparato che mi ha confuso:
Quando si sta cercando di ottenere l'inoltro perfetto, ti fare qualcosa di simile:Quando si perfeziona, il nome di tipo T diventa un T & o T &&, ma quando non lo si fa, T non è affatto un riferimento. Come?

template<class T> // If I were to call foo with an l-value int, foo would look 
void foo(T &&x); // like this: void foo(int& &&x) 

Così poi ho pensato, aspetta, vuol dire che se l'ho fatto:

template<class T> // If I were to call foo with an l-value int, foo would look 
void foo(T x); // like this: void foo(int& x); 

Ma non è quello che succede. foo appare invece in questo modo: void foo(int x);

La mia domanda: Come mai in funzione di inoltro perfetta, T si trasforma in un T o T & & &, ma nell'altro, T non è un punto di riferimento? Qualcuno può dirmi le regole esatte per questo? Ho bisogno di qualche chiarimento!

+0

Quando 'T &&' può diventare un valore l o un riferimento di valore r è ciò che Scott Meyers definisce" _Rapporto universale_ ". (Ritengo che questa terminologia stia ottenendo una maggiore adozione tra gli esperti.) Suggerisco di dare un'occhiata a [questo] (http://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012 -Scott-Meyers-Universal-References-in-Cpp11) presenta e legge il suo articolo in [Overload 111] (http://accu.org/var/uploads/journals/Overload111.pdf). –

risposta

9

Un modello parametro T può dedurre come un tipo di riferimento solo se compare in un parametro di funzione della forma T&&

Un modello di funzione della forma:

  • template<class T> void f(T x)
    dedurrà T come un tipo di oggetto (e x è un tipo di oggetto quindi viene passato per valore)

  • template<class T> void f(T& x)
    sarà dedurre T come un tipo di oggetto (e quindi x ha tipo riferimento lvalue)

  • template<class T> void f(T&& x)
    sarà dedurre T come

    • sia un lvalore di riferimento (quindi x trovi tipo di riferimento Ivalue causa riferimento regole collasso)
    • o come un tipo di oggetto (così x ha tipo riferimento rvalue)

Come mai nella trasmissione perfetta funzione, T diventa T & o T & &, [...]

Questo è sbagliato.T diventa un tipo riferimento L& o un tipo di oggetto R, non un riferimento R&&.
Il parametro di funzione della forma T&& diventa così

  • sia L& (perché l'aggiunta di un riferimento rvalue ad un riferimento Ivalue è ancora un riferimento lvalue, proprio come add_rvalue_reference<L&>::type è ancora L&)
  • o diventa R&& (perché è add_rvalue_reference<R>::typeR&&)
+0

Hai dato (IMHO) la risposta più facile da capire, quindi accetto il tuo. Grazie per aver chiarito questo per me. – Aaron

7

Questo è a causa del modo tipo deduzione è definita, ed è solo correlato all'inoltro perfetto nel senso che il risultato di std::forward<>() è un rvalue se viene passato un riferimento di rvalue e un lvalue se viene passato un riferimento di lvalue.

Ma in generale, quando non si dispone di un punto di riferimento per cominciare, il tuo T non sta per essere dedotta come un tipo di riferimento (vale a dire come A&, qualunque A potrebbe essere). Se così fosse, come indica correttamente Yakk nei commenti, sarebbe impossibile scrivere un modello di funzione che prende i suoi argomenti in base al valore.

In particolare, la regola riferimento collasso si fa riferimento è definito all'art 14.8.2.1/4 del C++ 11 standard:

Se P è un tipo riferimento, il tipo di cui con P si usa per la deduzione del tipo. Se P è un riferimento di rvalore a un parametro di modello cv-non qualificato e l'argomento è un lvalue, il tipo "riferimento di lvalue a A" viene utilizzato in posto di A per la deduzione di tipo. [Esempio:

template <class T> int f(T&&); 
template <class T> int g(const T&&); 
int i; 
int n1 = f(i); // calls f<int&>(int&) 
int n2 = f(0); // calls f<int>(int&&) 
int n3 = g(i); // error: would call g<int>(const int&&), which 
// would bind an rvalue reference to an lvalue 

-end esempio]

+0

+1 Picchiami con la citazione. –

+0

Perché potrebbe essere utile pure. Se 'template void foo (T t)' potrebbe dedurre 'T = int &', sarebbe impossibile dedurre una funzione template che ha preso argomenti per valore, perché c'è sempre un tipo di riferimento valido (sia esso 'T && ',' T & ',' T const & 'o' T const &&') per qualsiasi argomento. E dall'altra parte, la capacità di 'T' in' T && 'di essere dedotta come un tipo di riferimento rende possibile l'inoltro perfetto senza scrivere 4 funzioni ogni volta che si desidera farlo. Tuttavia, rende difficile scrivere una funzione che prende qualsiasi tipo come riferimento di rvalue. – Yakk

+0

@Yakk: È vero. Ne parlerò con un riferimento al tuo commento. Grazie –

6

Ci sono tre casi generali da considerare quando si dedurre parametri di modello come questo.

  1. void foo(T x): significa "pass-by-value". Si deduce sempre un tipo appropriato per passare per valore.

  2. void foo(T& x): questo significa "passaggio per valore di riferimento". Si deduce sempre un tipo appropriato per passare il riferimento lvalue.

  3. void foo(T&& x): significa "passaggio per riferimento". Si deduce sempre un tipo appropriato da passare per riferimento, che può essere un riferimento lvalue o un riferimento di rvalue.

6

Rilassarsi, rallentare e respirare.

Detrazione argomento modello è il meccanismo centrale che è necessario comprendere e non è del tutto banale. Quando si dice template <typename T> void foo(T), quindi T è sempre dedotto come un tipo non di riferimento.

Se volete un riferimento, è necessario mettere un & su di esso: template <typename T> void foo(T&) sarà anche dedurre T come un non-reference-tipo, ma foo ora si aspetta sempre un riferimento lvalue.

L'ultimo pezzo di magia deriva dalle nuove regole di collasso di riferimento. Quando si dice template <typename T> void foo(T&&), allora possono accadere due cose:

  • ti chiamano foo con una rvalue, per esempio foo(Bar()). Quindi T viene dedotto come Bar e foo prende un riferimento di rvalore a Bar, ad esempio Bar&&.

  • Si chiama foo con un lvalue, ad es. Bar x; foo(x);. Ora l'unica cosa che può fare foo è un riferimento lvalue. Ciò richiede T da dedurre come Bar&, dal T&& == Bar& && == Bar&, a causa delle regole di compressione.

Solo questo modello finale è in grado di accettare sia lvalue che rvalue. Questo è il motivo per cui viene talvolta chiamato "riferimento universale"; ma ricorda che non è il riferimento che conta, ma la deduzione argomento modello. L'utilizzo di std::forward<T> consente di trasmettere l'argomento con la stessa categoria di valore ricevuta.

Problemi correlati