2012-01-14 8 views
7

Sto guardando unique_ptr [di VC10] e fare un paio di cose che non capisco:Quando dovrei usare remove_reference e add_reference?

typedef typename tr1::remove_reference<_Dx>::type _Dx_noref; 

_Dx_noref& get_deleter() 
    { // return reference to deleter 
    return (_Mydel); 
    } 

unique_ptr(pointer _Ptr, 
    typename _If<tr1::is_reference<_Dx>::value, _Dx, 
     const typename tr1::remove_reference<_Dx>::type&>::_Type _Dt) 
    : _Mybase(_Ptr, _Dt) 
    { // construct with pointer and (maybe const) deleter& 
    } 

typename tr1::add_reference<_Ty>::type operator*() const 
    { // return reference to object 
    return (*this->_Myptr); 
    } 

Non sarebbe solo la scrittura _Dx & o _Ty & essere la stessa cosa?

Io in realtà capisco perché lo hanno fatto qui però:

unique_ptr(pointer _Ptr, typename tr1::remove_reference<_Dx>::type&& _Dt) 
    : _Mybase(_Ptr, _STD move(_Dt)) 
    { // construct by moving deleter 
    } 
+5

La risposta è che è necessario imparare il C++ e quindi si dovrebbe avere un'idea di come chiedere la cosa giusta. Mi dispiace se questo sembra duro, ma una volta che hai una presa più sicura sui modelli e sulla deduzione argomento, sarai in grado di capirlo. Ma non c'è alcun punto che salti in profondità, * specialmente * guardando un'implementazione di una biblioteca del mondo reale. È come cercare di imparare il greco antico scavando artefatti in Persia piuttosto che leggere un libro di testo. –

+7

@KerrekSB: A volte è molto più facile imparare da un singolo esempio ben spiegato che da un intero libro di testo. Inoltre, la maggior parte dei libri di testo non copre nemmeno questo materiale, perché sono stati scritti riferimenti -valore esistiti nella lingua. Questa è una domanda valida e ben fatta, che posso immaginare di essere utile per molte persone che stanno imparando C++. – Mankarse

+1

@KerrekSB Questo non è esattamente qualcosa che posso cercare. Anche se accadesse attraverso la regola che ha spinto il ctor a dover fare quello che fa probabilmente non lo assocerei qui senza vedere alcun esempio. In un'altra nota, puoi per favore smettere di fiammeggiare tutti i miei post? Hai chiaramente un problema con me (non mi interessa perché). – David

risposta

14

get_deleter

Ogni riferimento viene rimosso dal tipo di ritorno, poi si aggiunge un riferimento. In C++ 11 conforme, l'aggiunta di uno & a uno esistente & (o &&) produce uno &. In C++ 03, tuttavia, ciò costituirebbe un riferimento al tipo di riferimento, che era illegale. Probabilmente MSVC sta usando le vecchie regole, o quel codice è stato scritto quando lo ha fatto e rimane perché è innocuo.

costruttore

Qui rimuovere il riferimento, aggiungere const, e quindi aggiungere il riferimento di nuovo, per essere passando per const riferimento. Questo perché aggiungere const direttamente a un tipo di riferimento è nulla! (§8.3.2/1) In C++ 11 o C++ 03, la dichiarazione dei parametri sarebbe valida ma non aggiungerebbe uno const, se il riferimento non fosse stato rimosso e sostituito.

operator*

Questo è essenzialmente lo stesso come get_deleter, ma sono andati su di esso un modo diverso, e _Ty non può essere un tipo di riferimento per cominciare. Mi sembra come se _Ty& fosse sufficiente, ma è una loro prerogativa.

+1

Impressionante uomo in panne: grazie. Avrei alzato più volte se potessi, specialmente dopo che gli altri fiammeggiavano senza alcun motivo apparente. Per inciso, perché non puoi _Ti essere un tipo di riferimento per cominciare? – David

+0

@Dave: beh, '_Ty' * non dovrebbe * essere un tipo di riferimento perché non è possibile avere un puntatore a un riferimento. (8.3.2/5) Non sto verificando se ci sia qualche stranezza che serve a rimuovere un riferimento in quel caso, ma altrimenti il ​​membro 'unique_ptr' typedef' pointer' sarebbe mal formato. – Potatoswatter

+0

@Dave: nessuno ti ha infamato; prendere ciò che era destinato a essere una critica costruttiva così male è semplicemente sciocco. – ildjarn

2

Ecco un esempio, forse archetipo, per questo che abbiamo bisogno remove_reference, nella realizzazione di std::move: L'obiettivo è quello di restituire un tipo rvalue riferimento, sulla base del dedotto tipo di argomento della funzione.

Esempio: Foo x; move(x); Qui il numero move(x) deve restituire un tipo Foo&&. Ma l'argomento di move è un'espressione di tipo Foo&. Quindi, come può la funzione move dedurre il tipo giusto?

Il primo tentativo è quello di utilizzare ordinaria deduzione modello argomento e utilizzare un cast:

template <typename T> T && move(??? x) { return static_cast<T&&>(x); } 

Ma cosa deve andare in ???? Se diciamo T x, allora T sarà dedotto come Foo&; se diciamo T & x, quindi T = Foo, e se diciamo T && x, non corrisponderà affatto. La seconda versione, T & x, sembra essere utile.

Ma allora la funzione non funziona su rvalues ​​per cominciare (es move(Foo(...)). In questo caso, vogliamo T && x modo che T = Foo e T&& = Foo&& lo desideri. Potremmo avere due overload, ma avendo più sovraccarichi è indesiderabile perché aumenta la complessità inutilmente.Infine, se qualcuno specificasse esplicitamente il modello paramete come move<Foo&>(x), la funzione sarebbe mai, perché quando T = Foo&, quindi T&& = Foo&.

Così viene remove_reference:

template <typename T> 
typename std::remove_reference<T>::type && move(T && x) 
{ 
    return static_cast<typename std::remove_reference<T>::type &&>(x); 
} 

Prima di tutto, il nuovo riferimento collasso regole implicano che T è deduce come sia Foo& o Foo&& nei due casi. Quindi, remove_reference elimina il riferimento e restituisce il tipo Foo in qualsiasi caso e l'aggiunta di && rende il tipo di ritorno Foo&& desiderato.

In un riassunto semplificato: è necessario remove_reference perché (Foo&)&& è Foo& e non Foo&&. Se si scrive codice modello che richiede il tipo base di un parametro modello che può essere dedotto come U& o U&&, è possibile utilizzare questo modello.

1

template class add_reference ha specializzazione per void, const void e const volatile void causa riferimento void type (void&) non è consentito. Se viene utilizzato _Ty&, verrà generato un errore di compilazione quando _Ty = void.

Problemi correlati