2012-06-16 9 views
5

Vor C++ 11, wenn ich eine Funktion hätte, die an großen Objekten operiert, wäre mein Instinkt, Funktionen mit dieser Art von Prototyp zu schreiben.Gibt es in C++ 11 immer noch die Notwendigkeit, einen Verweis auf ein Objekt zu übergeben, das die Ausgabe einer Funktion akzeptiert?

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

(Hier return_value ist nur ein leeres Objekt, das die Ausgabe der Funktion erhalten. A nur einige Klasse ist, die groß und teuer zu kopieren.)

In C++ 11, wobei Vorteil bewegt Semantik, ist die Standard-Empfehlung (wie ich es verstehe) die einfacher:

A f(A const &parameter_value); 

gibt es überhaupt noch eine Notwendigkeit, es auf die alte Weise zu tun, in einem Objekt vorbei den Rückgabewert zu halten?

+1

Ich dachte, die Standard-Empfehlung war schon immer "mach was bequem ist und gehe aus dem Weg, wenn du es für nötig hältst" ... – Mehrdad

+3

Es gab noch nie so etwas, schau mal http://cpp-next.com/ archive/2009/08/want-speed-pass-by-value/ –

+0

@ K-ballo: Es könnte einen Vorstandard gegeben haben (obwohl die Leute es nicht anerkennen, C++ existierte fast 20 Jahre bevor es standardisiert wurde) . Obwohl vielleicht nicht, fing ich gerade an, C++ über die Zeit der Standardisierung zu lernen (und wusste nicht einmal, dass es zu der Zeit passierte), gab es so etwas wie Kopierschutz vor dem Standard? Wurde es von populären Compilern verwendet? –

Antwort

7

Andere haben den Fall behandelt, in dem A möglicherweise keinen billigen Move-Konstruktor hat. Ich gehe davon aus, dass Ihr A tut. Aber es gibt noch eine weitere Situation, wo Sie vielleicht in ein „out“ Parameter zu übergeben:

Wenn A ist eine Art wie vector oder string und es ist bekannt, dass die „out“ Parameter bereits Ressourcen (wie zB Speicher), die innerhalb f wiederverwendet werden können, dann ist es sinnvoll, diese Ressource wiederzuverwenden, wenn Sie können. Zum Beispiel berücksichtigen:

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); 
    } 
} 

Im ersten Fall Kapazität im string Aufbau wird als die Schleife ausführt, und dass die Kapazität möglicherweise wird dann bei jeder Iteration der Schleife wiederverwendet . Im zweiten Fall wird eine neue string bei jeder Iteration zugewiesen (unter Vernachlässigung des kleinen String-Optimierungspuffers).

Nun ist dies nicht zu sagen, dass Sie nie std::string nach Wert zurückgeben sollten.Nur dass Sie dieses Problem kennen und von Fall zu Fall technische Entscheidungen treffen sollten.

3

Es ist möglich, dass ein Objekt groß und teuer zu kopieren ist, und für das Verschieben Semantik kann nicht beim Kopieren verbessern. Bedenken Sie:

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

Es ist nicht eine gute Idee sein kann, Ihre Objekte auf diese Weise zu entwerfen, aber wenn Sie ein Objekt dieser Art aus irgendeinem Grund haben, und Sie wollen eine Funktion schreiben, um die Daten zu füllen dann könnten Sie mach es mit einem out-Parameter.

+1

Ich könnte erwägen, einen out-Parameter zu verwenden, aber nur, weil ich nicht viele davon auf dem Stack zuweisen möchte. Das Konstruieren eines Objekts aus dem Rückgabewert der Funktion ist so billig, wie es bei jedem normalen Compiler möglich ist. –

+0

@ eq-: niemand bittet dich, dies auf dem Stapel zuzuordnen, 'new f()' ist vollkommen akzeptabel. –

+0

@matthieum. Es gibt keine Möglichkeit, die Stapelzuordnung zu umgehen, wenn eine Funktion ein Objekt nach Wert zurückgibt. –

3

Es hängt davon ab: unterstützt Ihr Compiler die Rückgabewert-Optimierung, und ist Ihre Funktion f so konzipiert, dass Sie das RVO verwenden können, das Ihr Compiler unterstützt?

Wenn ja, dann ja, auf jeden Fall Wert zurück. Sie werden nichts erreichen, wenn Sie einen veränderlichen Parameter übergeben, und Sie werden eine Menge Code-Klarheit erlangen, wenn Sie es so machen. Wenn nicht, dann müssen Sie die Definition von A untersuchen.

Bei einigen Typen ist ein Zug nichts anderes als eine Kopie. Wenn A nichts enthält, was sich wirklich zu bewegen lohnt (Zeiger übertragen den Besitz usw.), dann werden Sie nichts gewinnen, wenn Sie umziehen. Eine Bewegung ist schließlich nicht frei; es ist einfach eine Kopie, die weiß, dass alles, was dem Original gehört, auf die Kopie übertragen wird. Wenn der Typ nichts besitzt, dann ist eine Bewegung nur eine Kopie.