2015-08-25 18 views
8

Perché il compilatore non è in grado di dedurre il parametro del modello per std::forward?Informazioni su std :: forward

voglio dire:

#include <memory> 
#include <iostream> 

struct X{}; 

struct A{ 
    A(const X&) { std::cout << "cpy ctor\n"; } 
    A(X&&) { std::cout << "move ctor\n"; } 
}; 

X foo() { return {}; } 

template<typename T,typename Arg> 
T* factory(Arg&& a) 
{ 
    return new T(std::forward(a)); 
    // ----------^^^^^^^^^^^^^^^ error: can't deduce template parameter 
} 

int main() 
{ 
    factory<A>(foo()); 
} 

So che questa è una scelta di progettazione (a causa della std::remove_reference nella definizione di std::forward) per evitare che l'utente dimentica di specificare il tipo. Quello che non riesco a ottenere è: perché il modo in cui viene implementato funziona per prevenire la detrazione? Perché il compilatore non si limita a dedurre il parametro del modello forward come Arg.

+4

Potete chiarire la domanda? Stai chiedendo perché il design è stato scelto per evitare la deduzione degli argomenti, o perché il modo in cui viene implementato funziona per prevenire la deduzione? – Angew

+1

Il punto di specificare manualmente il tipo è tale che 'forward' può decidere se deve" spostarsi "' a' o meno. La deduzione degli argomenti del modello ti consente di capire il tipo di 'a', ma non se deve essere spostato o meno. – nwp

+1

@Angew Il secondo. –

risposta

12

std::forward è dichiarata in questo modo:

template< class T > 
T&& forward(typename std::remove_reference<T>::type& t); 

typename std::remove_reference<T>::type è un non-dedotta contesto. Il compilatore non ha modo di sapere quale T deve essere dedotto perché non comprende la connessione semantica tra il tipo di membro type e un dato T. Avrebbe bisogno di cercare tra tutti i tipi per trovare una corrispondenza ed essere in grado di disambiguare in qualche modo le collisioni. Questo è irragionevole, quindi lo standard non lo consente.

+0

Oh, ok :) ----- – Quentin

+0

Sì, questo è quello che stavo chiedendo. Penso che leggere [questo] (http://stackoverflow.com/questions/25245453/what-is-a-nondeduced-context) farà il resto. –

6

Il motivo è necessario specificare un tipo per forward, in base alla progettazione, è ciò che accade al a all'interno della funzione:

template<typename T,typename Arg> 
T* factory(Arg&& a) 
{ 
    // 'a' is always an lvalue here 

Dal a è sempre valore assegnabile, non c'è abbastanza informazioni in a stesso per essere in grado di determinare se è stato passato come lvalue o rvalue. L'informazione è disponibile solo tramite il tipo Arg, che sarà X o X&. Senza che le informazioni di tipo in più, è impossibile sapere se la società a deve essere inoltrata come valore assegnabile o rvalue ... è per questo che è necessario fornire:

return new T(std::forward<Arg>(a)); 
} 
+4

Bene, pensavo che OP stesse facendo una domanda leggermente diversa. Lo lascerò qui comunque. – Barry

+0

Sì, mio ​​cattivo. Non era molto chiaro dalla domanda. Spero che la modifica aiuti a capire qual è stato il mio problema. –

0

Da C++ 11 di serie:

14.8.2.5 dedurre argomenti di template da un tipo

I contesti non dedotta sono:

- Il nested-nome-identificatore di un tipo specificato utilizzando una qualificata id

- L'espressione di uno specificatore di decltype.

- Un argomento modello non di tipo o un limite di matrice in cui una sottoespressione fa riferimento a un parametro modello.

- Un parametro modello utilizzato nel tipo di parametro di un parametro funzione che presenta un argomento predefinito utilizzato nella chiamata per la quale viene eseguita la deduzione argomento.

ecc ...

std::forward è dichiarata in questo modo:

template<typename _Tp> constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type& __t) noexcept

Secondo il primo frase di cui sopra: typename std::remove_reference<_Tp>::type è contesto non dedotta.