2015-12-30 19 views
6

Ho un unordered_set di caratterieffettiva costruzione std :: string da std :: unordered_set <char>

std::unordered_set<char> u_setAlphabet; 

Poi voglio ottenere un contenuto dal set come std :: string. La mia applicazione ora si presenta così:

std::string getAlphabet() { 
    std::string strAlphabet; 
    for (const char& character : u_setAlphabet) 
     strAlphabet += character; 
    return strAlphabet; 
} 

'questo un buon modo per risolvere questo compito? L'aggiunta di char signle alla stringa non sembra essere ottimale per il grande u_setAlphabet (multiple reallocs?). C'è qualche altro metodo?

+1

Come una questione di etichetta: un identificatore in un pezzo di codice di esempio minimo e rappresentativo non dovrebbe probabilmente essere scomodo come "u_setAlphabet". I buoni identificatori per il codice di esempio sono 'foo',' bar' e 'x'. –

+1

@KerrekSB D'altra parte, devo dire che preferisco il codice che copia un insieme non ordinato 'setAlphabet' in una stringa' strAlphabet' a uno che copia set (o was è stringa?) 'Foo' in stringa (o era impostato ?) 'bar'. – Angew

+0

@KerrekSB buon punto, ho in mente la tua raccomandazione alla prossima domanda. Alcune risposte utilizzano questi nomi ora, quindi non cambierò il contenuto della domanda qui – saleph

risposta

12

La risposta più semplice, più leggibile e più efficiente è:

return std:string(s.begin(), s.end()); 

L'implementazione può scegliere di rilevare la lunghezza del campo di up-front e solo allocare una volta; sia libC++ che libstdC++ fanno questo quando vengono dati un range di iteratore in avanti.

La classe string vi offre anche reserve, proprio come vector, per gestire la capacità di:

std::string result 
result.reserve(s.size()); 
for (unsigned char c : s) result.push_back(c); // or std::copy 
return result; 

Offre inoltre assign, append e insert funzioni membro, ma dal momento che coloro che offrono la garanzia forte eccezione, essi possono devi allocare un nuovo buffer prima di distruggere quello vecchio (grazie a @TC per evidenziare questo dettaglio cruciale!). L'implementazione di libC++ non si rialloca se la capacità esistente è sufficiente, mentre l'implementazione di libstdC++ di GCC5 si rialloca incondizionatamente.

+0

ouh, prenota, me ne sono dimenticato :) Ma il costruttore std :: string non lo ottimizza da solo? – saleph

+2

Vorrei raccomandare il codice 'reserve()' solo se sapete che siete davvero in un codice critico delle prestazioni e avete trovato una differenza misurandolo. Migliora il tuo codice, ma se il guadagno lo giustifica, va bene. Una buona implementazione della libreria standard riserva la memoria una sola volta anche con il costruttore che prende gli iteratori. L'unico overhead che otterrai è l'attraversamento per trovare la dimensione in primo piano. –

+3

@saleph No, come 'std :: set :: iterator' soddisfa solo BidirectionalIterator' std :: distance' è quindi un'operazione O (N). Tuttavia, 'std :: set :: size' è un'operazione a tempo costante. – Daniel

12

std::string ha a constructor per questo:

auto s = std::string(begin(u_setAlphabet), end(u_setAlphabet)); 
10

E 'meglio utilizzare il costruttore che acepts iteratori. Per esempio

std::string getAlphabet() { 
    return { u_setAlphabet.begin(), u_setAlphabet.end() }; 
} 
+0

è molto in stile C++ 11 :) Ma cosa pensi (secondo te) delle diffrenze di leggibilità tra il tuo e @xtofl risponderti? Giusto - la tua implementazione è la più breve, ma sembra una specie di magia al primo sguardo :) – saleph

+1

@saleph Se C++ ti sembra una magia per te allora dovresti imparare C++ :) Per quanto riguarda il confronto, allora 1) la risposta di xtofl è anche scritto usando le caratteristiche di C++ 11 e 2) non corrisponde alla domanda in cui si parla di definizione di una funzione. –

+0

Naturalmente capisco il tuo codice :) Ma la tua implementazione non è molto simile a quella di @KerrecSB? Costruisce una stringa, che verrà sicuramente spostata in seguito (ad esempio 'std :: string foo (getAlphabet());') – saleph

1

Entrambi return std::string(u_setAlphabet.begin(), u_setAlphabet.end()); e return { u_setAlphabet.begin(), u_setAlphabet.end(); sono gli stessi in C++ 11. Preferisco la soluzione @VladfromMoscow perché non è necessario fare alcuna ipotesi sul tipo restituito dell'oggetto temporaneo.

+0

ma questa costruzione nascosta non è meno chiara secondo te? Sono davvero mendicante in C++, quindi il mio senso di leggibilità non è molto esperto :) – saleph