2014-10-09 10 views
9

क्या सी ++ कंपाइलर्स वर्चुअल फ़ंक्शंस के लिए आरवीओ लागू करने में सक्षम हैं?क्या वर्चुअल फ़ंक्शन आरवीओ (वापसी मूल्य अनुकूलन) के लिए उम्मीदवार हो सकता है?

इस मामले में:

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 return result; लाइनों के लिए लागू कर सकते हैं? मुझे नहीं लगता।

फिर, std::move(result) उस मामले में बड़े कंटेनर लौटने के लिए जाने का तरीका है?

धन्यवाद

+1

क्या आप कृपया अपने प्रश्न को स्पष्ट कर सकते हैं? क्या आप अक्सर आभासी कार्यों को वापस करते हैं? – juanchopanza

+1

@juanchopanza: मुझे लगता है कि सवाल यह है कि आरवीओ वर्चुअल फ़ंक्शन _within_ काम करता है, यानी वर्चुअल फ़ंक्शन किसी भी चीज़ के लिए वर्चुअल फ़ंक्शन लौटने पर आरवीओ काम करता है या नहीं। (और मुझे कोई कारण नहीं दिख रहा है कि इसे सैद्धांतिक काम में क्यों नहीं होना चाहिए) – Damon

+0

@ डैमन मैं वही सोचता हूं, लेकिन ओपी को यह स्पष्ट करने दें कि वे वास्तव में क्या पूछना चाहते हैं। – juanchopanza

उत्तर

4

हाँ, संकलक RVO प्रदर्शन कर सकते हैं है। मैं कुछ परीक्षण कोड को पकाया जाता है और 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 प्रतिलिपि करने के लिए

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 

Conspicuously disassembly से अनुपस्थित किसी भी कॉल का उत्पादन होता है या M के निर्माता चलते हैं।


इसके अलावा, मानक प्रतिलिपि इलिजन के लिए मापदंड की स्थापना के पैरा आभासी और nonvirtual सदस्य कार्यों के बीच कोई फर्क ले जाती है, और जब भी प्रतिलिपि इलिजन के लिए मानक पूरा किया जाता है, return बयान "के लिए अधिभार संकल्प पहला है ऐसा किया गया कि ऑब्जेक्ट को एक रावल्यू द्वारा नामित किया गया था "।

कहने के लिए, एक समारोह में

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

प्रतिलिपि इलिजन भी कारण से नहीं हो सकती है, और एक कदम निर्माता उपलब्ध है, तो return m; हमेशा इस कदम निर्माता बल्कि प्रतिलिपि निर्माता से आह्वान जाएगा कि । इसलिए, यदि आप स्थानीय चर लौट रहे हैं तो वापसी विवरण के लिए std::move का उपयोग करने की आवश्यकता नहीं है।

+0

सही उत्तर, स्पष्ट और विस्तृत। – galinette

0

यदि आप return std::move(result);, आप कुछ भी हासिल नहीं कर सकते हैं, और आप खो सकते हैं। तो ऐसा मत करो।

तुम कुछ भी हासिल नहीं कर सकते, क्योंकि मानक स्पष्ट रूप से कहते हैं, "अगर RVO शर्तों को पूरा कर रहे हैं, या आप एक पैरामीटर के लिए लौट रहे, rvalue के रूप में लौटने की कोशिश पहली और एकमात्र कि अगर संकलन नहीं होगा, lvalue के रूप में वापसी। तो भले ही आप return result;, संकलक मजबूर अगर यह अन्य प्रकार से लागू था पहले। return std::move(result);

आप खो सकते हैं, return std::move(result); के बाद से विशेष रूप से RVO रोकता कोशिश करने के लिए।

+0

असल में, मामलों में आरवीओ संभव नहीं है, और यदि लौटा हुआ प्रकार चल रहा है और इसमें एक बड़ा अंतर्निहित बफर है, तो आप बहुत कुछ हासिल कर सकते हैं। यह प्रश्न का उत्तर भी नहीं है। – galinette

+2

@galinette नहीं, अगर प्रतिलिपि elision किसी भी कारण से नहीं किया जा सकता है, 'वापसी परिणाम;' तब भी एक कदम होगा जब संभव हो। –

+0

@galinette ठीक है, यदि आरवीओ संभव नहीं है, तो 'परिणाम' न तो स्थानीय चर है और न ही फ़ंक्शन का पैरामीटर है, इसलिए 'std :: move' का उपयोग करके इस मामले में कोई भी ब्रेनर नहीं है। आप आरवीओ के बारे में पूछ रहे थे, इसलिए मैंने एक ऐसी स्थिति संभाली जहां आरवीओ मानदंडों को पूरा किया गया। – Angew

संबंधित मुद्दे