2013-08-24 9 views
5

Ci sono già un buon numero di domande su unique_ptr e tipo incompleto su SO, ma nessuno può darmi un concetto per capire perché il seguente non lavoro:C++ 11: unique_ptr si lamenta del tipo incompleto, ma non quando lo avvolgo

// error: ... std::pair<...>::second has incomplete type 
template<typename K, typename T> struct Impl { 
    typedef typename std::unordered_map<K,Impl<K,T>>::iterator iter_type; 
    std::unique_ptr<iter_type> ptr; 
    Impl() : ptr(new iter_type()) {} 
}; 

int main() { Impl<int,int>(); return 0; } 

mentre la successiva fa:

template<typename K, typename T> struct Impl { 
    struct Wrapper { 
    typedef typename std::unordered_map<K,Impl<K,T>>::iterator iter_type; 
    iter_type iter; 
    }; 

    std::unique_ptr<Wrapper> ptr; 
    Impl() : ptr(new Wrapper()) {} 
}; 

int main() { Impl<int,int>(); return 0; } 

non vedo dove la differenza tecnica è: Se std::pair<...>::second (cioè, Impl<K,T>) era incompleta Impl nel primo esempio, dovrebbe essere incompleto t o Wrapper anche nel secondo. Inoltre, quando è sufficiente avvolgere il unique_ptr in una struttura, perché esiste una restrizione per il primo caso?

UPDATE:

Dopo la risposta di Dietmar Kühl, penso che il problema può essere ridotto al seguente:

template<typename K, typename T> struct Impl { 
    typename std::unordered_map<K,Impl<K,T>>::iterator ptr; 
}; 

vs

template<typename K, typename T> struct Impl { 
    struct Wrapper { 
     typename std::unordered_map<K,Impl<K,T>>::iterator iter; 
    }; 
    Wrapper *ptr; 
}; 

risposta

3

Il problema nel primo caso la il tipo incompleto viene utilizzato con std::unordered_map<K, Impl<K, T>: per determinare che cosa è iterator, parti di std::unordered_map<K, Impl<K, T> ne ed essere istanziato in un momento in cui Impl è semplicemente dichiarato. Il std::unique_ptr<...> non ha niente a che fare con l'errore. È possibile rimuovere l'uso di iter_type in quanto lo typedef deve verificare che si tratti di un tipo.

D'altra parte, quando si avvolge l'uso del tipo iteratore in Wrapper, questo tipo annidato non viene utilizzato prima dell'implementazione del costruttore. Naturalmente, le funzioni inline definite comportano come se la classe è stata appena completamente definito e sono attuate appena fuori della definizione della classe, cioè, il codice è equivalente ad

template<typename K, typename T> struct Impl { 
    struct Wrapper { 
    typedef typename std::unordered_map<K,Impl<K,T>>::iterator iter_type; 
    iter_type iter; 
    }; 

    std::unique_ptr<Wrapper> ptr; 
    Impl(); 
}; 
template<typename K, typename T> 
Impl<K, T>::Impl() : ptr(new Impl<K, T>::Wrapper()) {} 

Cioè, quando è necessaria la definizione di Wrapper e istanziato, è definito Impl.

+0

Intendi dire che la differenza è che il costruttore 'Wrapper' non è compilato prima che la classe' Impl' sia ** considerata ** completamente implementata? Quindi il Wrapper sta semplicemente rendendo artificialmente la costruzione dell'intero processo in due fasi quando l'ordine di compilazione potrebbe essere esattamente lo stesso? –

+0

@JoSo: i membri dei modelli di classe vengono istanziati quando utilizzati. 'Wrapper' è un membro di un modello di classe e viene utilizzato solo nella definizione del costruttore. Il 'typedef' non è realmente un membro e per definire il modello di classe' Impl' è una specie di istanza. Quindi, sì, rendendolo un processo a due stadi evita il problema del tipo non ancora definito. –

+0

Non dovrebbe esserci un modo per dire al compilatore "sì, questo tipo esiste davvero!" ?. Voglio dire, posso anche dire 'struct NewType; NewType * ptr; 'all'interno della classe senza mai definire NewType e funziona! Questa è solo una debolezza delle specifiche/implementazione o ci sono ragioni più profonde dietro di essa? –

Problemi correlati