2012-06-16 8 views
5

Prima di C++ 11, se avessi una funzione che operava su oggetti di grandi dimensioni, il mio istinto sarebbe quello di scrivere funzioni con questo tipo di prototipo.In C++ 11, c'è ancora la necessità di passare un riferimento a un oggetto che accetterà l'output di una funzione?

void f(A &return_value, A const &parameter_value); 

(Qui, return_value è solo un oggetto vuoto che riceverà l'output della funzione. A è solo una classe che è grande e costoso da copiare.)

In C++ 11, tenendo vantaggio della semantica mossa, la raccomandazione di default (se ho capito bene) è il più semplice:

A f(A const &parameter_value); 

c'è sempre ancora la necessità di farlo alla vecchia maniera, passando in un oggetto per contenere il valore di ritorno?

+1

Ho pensato che la raccomandazione predefinita è sempre stata "fai ciò che è conveniente e togliti di mezzo se lo trovi necessario" ... – Mehrdad

+3

Non c'è mai stata una tale necessità, controlla http://cpp-next.com/ archivio/2009/08/want-speed-pass-by-value/ –

+0

@ K-ballo: Potrebbe esserci stato un bisogno pre-standard (anche se le persone tendono a non riconoscerlo, il C++ esisteva quasi 20 anni prima che fosse standardizzato) . Anche se forse no, stavo solo iniziando ad imparare C++ sul tempo della standardizzazione (e non sapevo nemmeno che stesse succedendo in quel momento), c'era una cosa come elision di copia prima dello standard? È stato utilizzato dai compilatori popolari? –

risposta

7

Altri hanno coperto il caso in cui A potrebbe non avere un costruttore di movimento economico. Suppongo che lo sia il tuo A. Ma c'è ancora una situazione più in cui si potrebbe desiderare di passare un parametro "out":

Se A è un certo tipo, come vector o string ed è noto che il parametro "out" ha già le risorse (ad esempio la memoria) che può essere riutilizzato entro f, quindi ha senso riutilizzare quella risorsa se è possibile. Ad esempio si consideri:

void get_info(std::string&); 
bool process_info(const std::string&); 

void 
foo() 
{ 
    std::string info; 
    for (bool not_done = true; not_done;) 
    { 
     info.clear(); 
     get_info(info); 
     not_done = process_info(info); 
    } 
} 

vs:

std::string get_info(); 
bool process_info(const std::string&); 

void 
foo() 
{ 
    for (bool not_done = true; not_done;) 
    { 
     std::string info = get_info(); 
     not_done = process_info(info); 
    } 
} 

Nel primo caso, la capacità si accumula nel string come il ciclo, e che la capacità viene quindi potenzialmente riutilizzato su ogni iterazione del ciclo . Nel secondo caso viene assegnato un nuovo string ad ogni iterazione (trascurando il buffer di ottimizzazione delle stringhe piccole).

Ora questo non vuol dire che non si dovrebbe mai restituire std::string in base al valore.È sufficiente essere consapevoli di questo problema e applicare il giudizio di ingegneria caso per caso.

3

È possibile che un oggetto sia grande e costoso da copiare, e per il quale la semantica di spostamento non può migliorare sulla copia. Considerare:

struct A { 
    std::array<double,100000> m_data; 
}; 

potrebbe non essere una buona idea per progettare gli oggetti in questo modo, ma se si dispone di un oggetto di questo tipo per qualche motivo e si vuole scrivere una funzione per riempire i dati in allora si potrebbe fallo usando un parametro out.

+1

Potrei prendere in considerazione l'utilizzo di un parametro out, ma solo perché non vorrei allocare molti di quelli in pila. Costruire un oggetto dal valore di ritorno della funzione è tanto economico quanto lo è su qualsiasi compilatore sano di mente. –

+0

@ eq-: nessuno ti chiede di allocarlo nello stack, 'new f()' è perfettamente accettabile. –

+0

@matthieum. Non c'è modo di escludere l'allocazione dello stack quando una funzione restituisce un oggetto per valore. –

3

Dipende: il tuo compilatore supporta l'ottimizzazione del valore di ritorno ed è la tua funzione f progettata per essere in grado di utilizzare il RVO supportato dal compilatore?

Se sì, allora sì, con tutti i mezzi restituire in base al valore. Non otterrai nulla passando un parametro mutabile e otterrai una grande quantità di chiarezza del codice, facendo così. In caso contrario, è necessario esaminare la definizione di A.

Per alcuni tipi, una mossa non è altro che una copia. Se lo A non contiene nulla che valga la pena di essere spostato (puntatori che trasferiscono la proprietà e così via), allora non otterrai nulla spostando. Dopo tutto, una mossa non è libera; è semplicemente una copia che sa che tutto ciò che è di proprietà dell'originale viene trasferito alla copia. Se il tipo non possiede nulla, allora una mossa è solo una copia.

Problemi correlati