2014-10-09 5 views
9

Können C++ - Compiler RVO für virtuelle Funktionen anwenden?Kann eine virtuelle Funktion ein Kandidat für RVO sein (Rückgabewertoptimierung)?

In diesem Fall:

class AbstractReader 
{ 
//... 
public: 
    virtual std::vector<float> getFloatVector() = 0; 
//... 
} 

class XmlReader : public AbstractReader 
{ 
//... 
public: 
    virtual std::vector<float> getFloatVector() 
    { 
     std::vector<float> result; 

     //Do some parsing here... 

     return result; 
    } 
//... 
} 



class BinaryReader : public AbstractReader 
{ 
//... 
public: 
    virtual std::vector<float> getFloatVector() 
    { 
     std::vector<float> result; 

     //Do some decoding here... 

     return result; 
    } 
//... 
} 

RVO Kann return result; Linien anwenden? Ich würde es nicht erraten.

Dann ist std::move(result) der Weg für die Rückgabe großer Container in diesem Fall?

Dank

+1

Können Sie bitte Ihre Frage klären? Gibt es oft virtuelle Funktionen? – juanchopanza

+1

@juanchopanza: Ich denke, die Frage ist, ob RVO _within_in einer virtuellen Funktion, d. H. Für alles, was die virtuelle Funktion möglicherweise zurückgibt, nicht, ob RVO funktioniert, wenn eine virtuelle Funktion zurückgibt. (Und ich sehe keinen Grund, warum das prinzipiell nicht funktionieren sollte) – Damon

+0

@Damon Ich denke das gleiche, aber besser lassen OP erklären was sie eigentlich fragen wollen. – juanchopanza

Antwort

4

Ja, die Compiler RVO durchführen kann. Ich kochte einige Tests Code auf und lief es durch godbolt:

struct M { 
    M(); 
    M(const M&); 
    M(M &&); 
    ~M(); 
    double * ptr; 
}; 

M getM(); 

struct A { 
    virtual M foo() = 0; 
}; 

struct B : A { 
    virtual M foo() override; 
}; 

M B::foo(){ 
    M m; 
    return m; 
} 

struct C : B { 
    virtual M foo() override; 
}; 
M C::foo(){ 
    M m = getM(); 
    return m; 
} 

A* getA(); 

int main(){ 
    A* p = getA(); 
    M m = p->foo(); 
} 

g++ -O3 produziert

B::foo(): 
    pushq %rbx 
    movq %rdi, %rbx 
    call M::M() 
    movq %rbx, %rax 
    popq %rbx 
    ret 
C::foo(): 
    pushq %rbx 
    movq %rdi, %rbx 
    call getM() 
    movq %rbx, %rax 
    popq %rbx 
    ret 
main: 
    subq $24, %rsp 
    call getA() 
    movq (%rax), %rdx 
    movq %rax, %rsi 
    movq %rsp, %rdi 
    call *(%rdx) 
    movq %rsp, %rdi 
    call M::~M() 
    xorl %eax, %eax 
    addq $24, %rsp 
    ret 

unübersehbar fehlt der Demontage ist jeder Aufruf der Kopie oder Konstruktor M bewegen.


Auch der Absatz der Norm legt die Kriterien für die Kopie elision zieht zwischen virtuellen und nicht virtuellen Elementfunktionen darlegt keinen Unterschied, und immer dann, wenn der Standard für die Kopie elision erfüllt ist, die Überladungsauflösung für die return Aussage „ist zuerst so ausgeführt, als wäre das Objekt mit einem rvalue gekennzeichnet ".

, dass in einer Funktion

M foo() { 
    M m = /*...*/; 
    return m; 
} 

Wenn Kopie elision nicht stattfinden kann, aus welchem ​​Grund, und ein Umzug Konstruktor zur Verfügung steht, return m; immer aufrufen, um den Umzug Konstruktor statt der Copykonstruktor sagen . Daher müssen Sie für die Rückgabeanweisung std::move nicht verwenden, wenn Sie eine lokale Variable zurückgeben.

+0

Perfekte Antwort, klar und detailliert. – galinette

0

Wenn Sie return std::move(result);, kann man nichts gewinnen, und Sie können verlieren. Also tu es nicht.

Sie können nichts gewinnen, weil der Standard ausdrücklich sagt: „Wenn RVO Bedingungen erfüllt sind, oder Sie einen Parameter zurückkehren, versuchen, so rvalue Rückkehr erste und nur wenn das nicht kompilieren würde, zurück, wie lvalue. Also selbst wenn Sie return result;, die Compiler gezwungen sind, zu versuchen return std::move(result); zuerst.

Sie verlieren können, da return std::move(result); speziell RVO verhindert, wenn es anders anwendbar war.

+0

Eigentlich ist RVO nicht möglich, und wenn der zurückgegebene Typ beweglich ist und einen großen zugrunde liegenden Puffer hat, können Sie viel gewinnen. Dies ist auch keine Antwort auf die Frage. – galinette

+2

@galinette Nein, wenn die Kopie aus irgendeinem Grund nicht ausgeführt werden kann, wird 'return result;' immer noch einen Zug ausführen, wenn dies möglich ist. –

+0

@galinette Nun, wenn RVO nicht möglich ist, dann ist 'result' weder eine lokale Variable noch ein Parameter der Funktion, also ist' std :: move' in diesem Fall sowieso kein Kinderspiel. Sie haben nach RVO gefragt, also habe ich eine Situation angenommen, in der die RVO-Kriterien erfüllt sind. – Angew