2011-01-19 15 views
8

scusate per una domanda così lunga ma cerco di essere il più chiaro possibile. Questo in qualche modo segue la mia domanda precedente su strings in C++. Sto cercando di capire come potrei restituire std :: string da una funzione senza allocazioni di memoria ridondanti, senza fare affidamento su NRVO. I motivi per cui non voglio fare affidamento su NRVO sono:STL swap on return?

  • non è supportato dal compilatore attualmente usiamo
  • anche quando è supportata potrebbe non sempre essere attivata in modalità debug
  • potrebbe non riuscire in alcuni casi (example)

si prega di notare che ho bisogno di una soluzione compatibile C++ 03 (senza C++ 0x rvalue riferimenti in tal modo, purtroppo ...)

Il sI mplest modo fare questo è pass-by-di riferimento e fare std :: di swap, come questo

void test(std::string& res) 
{ 
    std::string s; 
    //... 
    res.swap(s); 
} 

Ma è più naturale e spesso più comodo per ritornare al valore di passare per riferimento, quindi quello che voglio raggiungere è questo:

std::string test() 
{ 
    std::string s; 
    //... 
    return SOMETHING(s); 
} 

ideale sarebbe basta fare un swap con il "valore di ritorno", ma non vedo come fare questo in C++. C'è già auto_ptr che si muove invece di copiare, e potrei effettivamente usare auto_ptr<string>, ma vorrei evitare di allocare dinamicamente l'oggetto stringa stesso.

mia idea è quella in qualche modo "tag" un oggetto stringa che viene riconsegnato da una funzione per consentire lo spostamento suoi dati quando un costruttore di copia è chiamato al ritorno. Così ho finito con questo codice, che fa esattamente quello che voglio:

struct Str 
{ 
    struct Moveable 
    { 
     Str & ref; 
     explicit Moveable(Str & other): ref(other) {} 
    }; 

    Str() {} 
    Str(const std::string& other) : data(other) {} // copy 
    Str(Moveable& other) { data.swap(other.ref.data); } // move 

    Moveable Move() 
    { 
     return Moveable(*this); 
    } 

    std::string data; 
}; 

Str test() 
{ 
    Str s; 
    //... 
    return s.Move(); // no allocation, even without NRVO 
} 

Quindi ... fa tutto questo ha senso, o ci sono alcuni problemi seri che mi manca? (Non sono sicuro che non ci siano problemi di durata, ad esempio). Forse hai già visto questa idea in una biblioteca (libro, articolo ...) e potresti darmi un riferimento?

MODIFICA: come notato da @rstevens, questo codice è specifico per MSVC e non verrà compilato in g ++ che non apprezza il temporaneo non const. Questo è un problema, ma supponiamo che questa implementazione sia specifica per MSVC.

+0

Non sono sicuro di cosa stai chiedendo. Hai solo bisogno di correggere il tuo codice? Dici che fa esattamente quello che vuoi ma, per quanto posso vedere, non dovrebbe essere compilato. –

+2

Hai controllato se la tua stringa fa COW. In tal caso ci saranno alcune allocazioni di memoria aggiuntive. Inoltre alcune implementazioni di std :: string metteranno la stringa nell'oggetto (quando la stringa è breve) piuttosto che allocare memoria per essa. Se ciò è vero, i tuoi tentativi di ottimizzazione potrebbero aumentare il costo piuttosto che ridurlo. –

+0

@Charles: Il codice è lì per spiegare cosa sto cercando di ottenere, e la domanda è se la vedo bene e se ci sono già alcune buone implementazioni di questo. –

risposta

4

L'implementazione di boost utilizza l'emulazione della semantica per le librerie come Boost.Thread. Potresti voler esaminare l'implementazione e fare qualcosa di simile.

Modifica: c'è in realtà uno sviluppo attivo di una libreria Boost.Move, quindi è già possibile iniziare a usarlo.

0

Avete effettivamente stabilito che il ritorno per valore è un problema di prestazioni nella vostra applicazione? Sembra il modo più semplice/più semplice e quando si esegue l'aggiornamento a un compilatore più moderno è possibile utilizzare i riferimenti rvalue.

Non riesco a rispondere alla domanda riguardante l'ordine di distruzione di s rispetto al riferimento di Movable. Potresti, per il tuo compilatore, inserire il codice nei vari costruttori e distruttori per vedere qual è l'ordine.Anche se sembra ok, continuerei a considerare l'utilizzo di uno dei pattern normali che hai delineato, anche se solo per prevenire la confusione del lettore e magari rompere con un compilatore alternativo.

+1

Per me non è solo una questione di ottimizzazione, poiché influenza l'utilizzo tipico - a meno che gli sviluppatori non sappiano che il ritorno di valore è economico, cercheranno di evitare di farlo. Le persone cercano ancora di scrivere un codice un po 'ottimale, anche se dici che l'ottimizzazione prematura è malvagia. –

1

Hai controllato questo codice su g ++?

Poiché si chiama Str (Movable &) con un oggetto temporaneo (quello restituito da s.Move())!

Questo non è conforme allo standard e non è supportato da g ++. È supportato da MSVC! (MS chiama questa funzione ...).

+0

Hai ragione, non verrà compilato sotto g ++, ho aggiornato la domanda. Qualcosa di diverso può probabilmente essere fatto per g ++, ma sono più interessato a sapere se qualcosa * simile * è usato da qualche parte, non esattamente la mia implementazione di esempio. –