2015-11-06 4 views
5

Wenn ich eine Funktion zurückzukehren mir einen Container will:Return-Value-Optimierung: ho kann ich Kopie Konstruktion von großen STL-Container vermeiden.

vector<T> func(){ 
    vector<T> result; 
    ... 
    return result; 
} 

Um auf folgende Weise verwendet werden:

vector<T> result = func(); 

Um den Aufwand für das Kopieren von meinem Behälter zu vermeiden ich die oft schreiben Funktion, so dass es nichts zurückgibt, aber akzeptieren Sie eine nicht konstante Instanz des Containers.

vector<T> result; 
func(result); 

Ist meine Anstrengung sinnlos, weil ich sicher sein kann, dass der Compiler-Optimierung verwendet immer den Rückgabewert:

void func(vector<T>& result){ 
    result.clear(); 
    ... 
    result; 
} 

Um in der folgenden Art und Weise verwendet werden?

+1

Seit C++ 11 ist Ihr Aufwand bedeutungslos, bis C++ 11 der richtige Weg ist. – 101010

+0

Sie sollten Ihr erstes Codebeispiel auch in C++ 03 verwenden. – Simple

+0

Sie gehen davon aus, dass RVO, wenn angewendet, genauso effizient wie der andere Weg wäre. Das ist nicht immer der Fall. –

Antwort

10

Es ist bedeutungslos. Der von Ihnen erwähnte RVO-Typ heißt RVO (NRVO), und die meisten Compiler implementieren ihn.

Unabhängig davon, in C++ 11, vector hat Move Konstruktoren, also selbst wenn NRVO nicht zutreffen würde, würde es immer noch verschoben, nicht kopiert werden.

2

RVO ist nicht garantiert, aber anständige Compiler werden es verwenden, wenn es erlaubt ist.

Das Problem ist jedoch RVO hilft nur, wenn Sie ein neues Objekt außerhalb der Funktion erstellen. Wenn Sie den gleichen Vektor wiederverwenden, indem Sie ihn als Referenz übergeben, können Sie die reservierte Kapazität nutzen, um die Anzahl der Speicherzuweisungen zu reduzieren. Ein lokaler Vektor, der innerhalb der Funktion erstellt wird, muss immer intern einen neuen Puffer zuweisen, unabhängig davon, wo der Rückgabewert gespeichert ist. Daher kann es effizienter sein, den Vektor als Referenz zu übergeben, obwohl der Code weniger gut aussieht.

2

Hängt vom Alter Ihres Compilers ab. Vor C++ 11 ist Ihr alternativer Ansatz erforderlich, es sei denn, der Compiler unterstützt die benannte Rückgabewertoptimierung - was nicht alle älteren Compiler tun. Sie können auch die Funktion einen Verweis auf den übergebenen Vektor zurückgeben lassen.

Ab C++ 11 unterstützt die Sprache die Konstruktion von Bewegungen, und die Standardcontainer verfügen über funktionierende Move-Konstruktoren. Ihre erste Vorgehensweise ist also in Ordnung. Puristen werden darauf bestehen, dass das besser ist. Pragmatiker (die erkennen, dass nicht jeder ihre Compiler ohne einen riesigen Impost updaten kann) werden sagen, dass sie eine Lösung wählen müssen, abhängig davon, ob ihr Code mit einer Mischung aus C++ 11 und späteren Compilern weiter arbeiten soll.

+0

Selbst wenn Sie einen älteren Compiler verwenden, können Sie herausfinden, ob die Kopie Elision implementiert. –

+0

In der Tat, M.M. Oder lesen Sie die Compiler-Dokumentation. Es gibt jedoch immer noch Code, der mit mehreren Compilern (sogar Compilerversionen und -einstellungen), zwischen Betriebssystemen usw. arbeiten muss. – Peter

0

Ich habe es mit gcc versucht. Ich habe festgestellt, dass ich mich beim Kompilieren ohne C++ 11 Flags nicht auf NRVO verlassen kann.

Da Ich mag nicht die zweite Signatur (wobei die Funktion, den Behälter durch Bezugnahme nimmt) kam ich damit aus:

die Funktion in seiner natürlichen Form deklarieren:

vector<T> func(){ 
    vector<T> result; 
    ... 
    return result; 
} 

und, wenn ich nicht sicher bin, über den Compiler und die Kompilation Fahnen, es auf diese Weise verwenden:

vector<T> result; 
func().swap(result) 

auf diese Weise erhält man die gewünschte Schnittstelle und ist sicher, elidible Stolpern zu vermeiden Köpfe.

Beachten Sie, dass die Kapazität des Vektors result der Vektor ist, der von der Funktion zurückgegeben wird. Wenn man die Kapazität für den Vektor einstellen möchte, ist die rechte Schnittstelle für die Funktion die zweite.

+0

Sowohl das Tauschen als auch das Übergeben einer Referenz sehen hässlich aus. Sie können sich auch an die Referenzmethode halten. –

+0

IMO Swapping ist besser als die Referenzmethode, da das Swapping die Deklaration nicht beinhaltet. Die Signatur der Funktion ist viel besser lesbar (nimmt in meinem Beispiel nichts einen Vektor zurück). – jimifiki

+0

Referenzmethode kann die vorhandene Vektorkapazität nutzen. In Hochleistungsanwendungen müssen Sie manchmal etwas Code-Nice- ness für Geschwindigkeit opfern. –