2012-01-06 6 views
5

Un contenitore di unique_ptr sembra avere poco senso: non è possibile utilizzarlo con gli elenchi di inizializzatore e non è riuscito a scorrere il contenitore (soluzioni alternative in basso). Sto fraintendendo qualcosa? O quando ha senso usare i contenitori unique_ptr e STL?Quando ha senso usare unique_ptr con i container STL? (C++ 11)

#include <memory> 
#include <vector> 

using namespace std; 

struct Base { void go() { } virtual ~Base() { } }; 
// virtual ~Base() = default; gives 
// "declared virtual cannot be defaulted in the class body" why? 

class Derived : public Base { }; 

int main() { 

    //vector<unique_ptr<Base>> v1 = { new Derived, new Derived, new Derived }; 
    //vector<shared_ptr<Base>> v2 = { new Derived, new Derived, new Derived }; 
    vector<Base*> v3 = { new Derived, new Derived, new Derived }; 
    vector<shared_ptr<Base>> v4(v3.begin(), v3.end()); 
    vector<unique_ptr<Base>> v5(v3.begin(), v3.end()); 

    for (auto i : v5) { // works with v4 
    i->go(); 
    } 
    return 0; 
} 


Le seguenti domande aiutato a trovare queste soluzioni alternative:

risposta

14
for (auto i : v5) { 
    i->go(); 
} 

Dovrebbe essere

for (auto& i : v5) { // note 'auto&' 
    i->go(); 
} 

Altrimenti si tenta di copiare l'elemento corrente.

Inoltre, non è possibile utilizzare un elenco di inizializzazione come quello, poiché i costruttori di std::unique_ptr e std::shared_ptr sono contrassegnati con explicit. Hai bisogno di fare qualcosa di simile:

#include <iterator> // make_move_iterator, begin, end 

template<class T> 
std::unique_ptr<T> make_unique(){ // naive implementation 
    return std::unique_ptr<T>(new T()); 
} 

std::unique_ptr<Base> v1_init_arr[] = { 
    make_unique<Derived>(), make_unique<Derived>(), make_unique<Derived>() 
}; 

// these two are only for clarity 
auto first = std::make_move_iterator(std::begin(v1_init_arr)); 
auto last = std::make_move_iterator(std::end(v1_init_arr)); 
std::vector<std::unique_ptr<Base>> v1(first, last); 

std::vector<std::shared_ptr<Base>> v2 = { 
    std::make_shared<Derived>(), 
    std::make_shared<Derived>(), 
    std::make_shared<Derived>() 
}; 

E questa è una buona cosa ™, perché altrimenti si potrebbe perdere la memoria (se uno dei costruttori più tardi tiri liberi, gli ex quelli non sono ancora vincolati ai puntatori intelligenti). Il tip-toeing per il unique_ptr è necessario, perché gli elenchi di inizializzatori copiano i loro argomenti e dal momento che gli unique_ptr s non sono copiabili, si otterrebbe un problema.


Detto questo, io uso un std::map<std::string, std::unique_ptr<LoaderBase>> per un dizionario di caricatori in uno dei miei progetti.

+1

'vector > v1 = {make_unique (), make_unique (), make_unique ()};' <- Avete provato questo? –

+0

Come allude @Benjamin, gli elenchi di inizializzatori eseguono copie per definizione, quindi non possono essere utilizzati con oggetti di sola spostamento. – ildjarn

+0

@ Benjamin: Certo, no, dato che al momento non ho a disposizione alcun compilatore che supporti elenchi di inizializzatori del genere.Tuttavia, non vedo quale problema potrebbe sorgere? – Xeo

1

unique_ptr ha senso nei contenitori STL quando il contenitore contiene oggetti che non possono essere copiati. O se è costoso o semplicemente scorretto copiarli.

È possibile ottenere la stessa funzionalità vostro

vector<Base*> v3 = { new Derived, new Derived, new Derived }; 

Ma senza la memoria perdite che v3 è invitante.

+1

Oppure quando hai bisogno di un comportamento polimorfico. – ildjarn

+0

È possibile inserire oggetti non copiabili (come 'unique_ptr') in contenitori C++ 11; anche se alcuni contenitori richiedono che siano mobili. –

1

In realtà è possibile ripetere il contenitore senza problemi utilizzando std::unique_ptr<T> ... è sufficiente accedere a un riferimento (ovvero non una copia) del puntatore univoco, oppure è necessario utilizzare effettivamente un tipo di iteratore con il contenitore. Nel tuo caso sarebbe qualcosa come vector<unique_ptr<Base>>::iterator o vector<unique_ptr<Base>>::const_iterator.