2014-09-24 6 views
11

Etwas überraschend (für mich), die folgenden zwei Programme zu unterschiedlichen Ausgängen kompilieren, wobei letztere eine viel bessere Leistung aufweisen (mit gcc und Klirren getestet):Automatische xValue Optimierung

#include <vector> 
int main() 
{ 
    std::vector<int> a(2<<20); 
    for(std::size_t i = 0; i != 1000; ++i) { 
     std::vector<int> b(2<<20); 
     a = b; 
    } 
} 

vs.

#include <vector> 
int main() 
{ 
    std::vector<int> a(2<<20); 
    for(std::size_t i = 0; i != 1000; ++i) { 
     std::vector<int> b(2<<20); 
     a = std::move(b); 
    } 
} 

Könnte mir jemand erklären, warum der Compiler tut (oder können) nicht automatisch b ein xValue in der letzten Zuweisung betrachten und zu bewegen Semantik ohne ausdrückliche std::move Guss anwenden?

bearbeiten: Zusammengestellt mit (g++|clang++) -std=c++11 -O3 -o test test.cpp

+0

Welche Parameter übergeben Sie an die Compiler? – Joe

+1

Meine erste Vermutung ist, dass dies die Semantik des Programms auf eine unerwartete Weise ändern würde, indem man eine Kopie in eine Bewegung umwandelt. – pmr

+0

@pmr: Das vermute ich auch, aber ich würde wirklich gerne verstehen warum. Naiv scheint es genau zu sein, was ein xvalue für mich sein sollte. – Xoph

Antwort

6

Compilers can't break the as-if rule

Als §1.9/1 heißt es:

Die semantischen Beschreibungen in dieser Internationalen Norm eine parametrisierte nondeterministic abstrakte Maschine definieren. Dieser internationale Standard stellt keine Anforderung an die Struktur von übereinstimmenden Implementierungen. Insbesondere müssen sie die Struktur der abstrakten Maschine nicht kopieren oder emulieren. Vielmehr sind konforme Implementierungen erforderlich (nur) das beobachtbare Verhalten der abstrakten Maschine zu emulieren, wie unten

erklärt

heißt ein Compiler kann nicht das beobachtbare Verhalten des Programms ändern. Die Konvertierung einer Zuweisung in eine Zugzuweisung würde diese Anweisung automatisch (auch ohne Rückwirkungen) brechen.

Kopierfehler können dieses Verhalten leicht ändern, aber dies wird durch §12.8/31 geregelt.

Wenn Sie die move-Version verwenden möchten, müssen Sie wie im letzten Beispiel explizit danach fragen.

+1

OK, so sollte insbesondere der Programmierer zuverlässige Kopie/Move Operator/Ctor Calling haben. Ich nahm irgendwie an, dass es vernünftig wäre, vom Programmierer zu verlangen, diese Operationen semantisch kompatibel zu machen. Ich denke, beide Ansätze hätten ihre Vor- und Nachteile, aber ich sehe, warum der Standard das anders sieht. – Xoph

+2

In diesem spezifischen Code würde die as-if-Regel nicht unterbrochen, da keine Ausgabe erfolgt –

5

die bei der nächsten Probe Werfen wir einen Blick (ignorieren Sie bitte void Rückgabetyp von operator=):

#include <iostream> 

struct helper 
{ 
    void operator=(helper&&){std::cout<<"move"<<std::endl;} 
    void operator=(const helper&){std::cout<<"copy"<<std::endl;} 
}; 

void fun() 
{ 
    helper a; 
    { 
     helper b; 
     a = b; 
    } 
} 

void gun() 
{ 
    helper a; 
    { 
     helper b; 
     a = std::move(b); 
    } 
} 
int main() 
{ 
    fun(); 
    gun(); 
} 

Das operator= anderes Verhalten hat auf seine Argumente abhängig. Der Compiler darf den Code nur dann optimieren, wenn er das beobachtbare Verhalten gleich halten kann.

Anbetracht b von fun ein xvalue, während es keine xvalue im Moment des Anrufs ist, wird es das beobachtbare Verhalten des Programms ändern und dies wird nicht noch von der Norm erlaubt gewünscht.

+0

Danke, ich war mir der Änderung im Aufruf des Konstruktors bewusst. Ich nahm an, dass der Programmierer irgendwie gezwungen wurde, Move-Konstruktoren/-Operatoren zu erstellen und Konstruktoren/Operatoren "semantisch passend" zu kopieren, und ich denke, das ist mein Fehler hier! – Xoph