2014-10-09 26 views
9

Trình biên dịch C++ có thể áp dụng RVO cho các chức năng ảo không?Chức năng ảo có thể là ứng cử viên cho RVO (tối ưu hóa giá trị trả về) không?

Trong trường hợp này:

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 có thể áp dụng đối với return result; dòng? Tôi đoán là không.

Sau đó, là std::move(result) cách để trả lại các thùng chứa lớn trong trường hợp đó?

Cảm ơn

+1

Bạn có thể làm rõ câu hỏi của mình không? Bạn có thường trả về các chức năng ảo không? – juanchopanza

+1

@juanchopanza: Tôi nghĩ câu hỏi là liệu RVO có hoạt động _within_ một chức năng ảo hay không, tức là đối với bất kỳ chức năng ảo nào có thể trả lại, không cho dù RVO hoạt động khi trả về một hàm ảo. (Và tôi không thấy lý do tại sao không nên làm việc theo nguyên tắc) – Damon

+0

@Damon Tôi nghĩ tương tự, nhưng tốt hơn hãy để OP giải thích những gì họ thực sự muốn hỏi. – juanchopanza

Trả lời

4

Vâng, trình biên dịch có thể thực hiện RVO. Tôi nấu chín lên một số mã thử nghiệm và chạy nó thông qua 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 sản xuất

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 

vắng mặt tháo gỡ bất kỳ cuộc gọi đến sao chép hoặc di chuyển constructor của M.


Ngoài ra, các đoạn của đặt ra các tiêu chuẩn cho bản sao sự bỏ bớt tiêu chuẩn thu hút có sự phân biệt giữa các hàm thành viên ảo và nonvirtual, và bất cứ khi nào chuẩn cho bản sao sự bỏ bớt được đáp ứng, độ phân giải quá tải cho các tuyên bố return "là đầu tiên thực hiện như thể đối tượng được chỉ định bởi một giá trị ".

Đó là để nói, trong một chức năng

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

Nếu copy sự bỏ bớt không thể diễn ra vì lý do gì, và một constructor Động thái này là có sẵn, return m; sẽ luôn luôn gọi các nhà xây dựng di chuyển chứ không phải copy constructor . Do đó, không cần sử dụng std::move cho câu lệnh trả về nếu bạn trả về biến cục bộ.

+0

Câu trả lời hoàn hảo, rõ ràng và chi tiết. – galinette

0

Nếu bạn return std::move(result);, bạn không thể đạt được bất cứ điều gì, và bạn có thể mất. Vì vậy, không làm điều đó.

Bạn không thể đạt được bất cứ điều gì, bởi vì tiêu chuẩn rõ ràng nói "nếu điều kiện RVO được đáp ứng, hoặc bạn đang trả lại một tham số, cố gắng trở về như rvalue đầu tiên và chỉ khi đó sẽ không biên dịch, trở lại như vế trái. vì vậy, ngay cả khi bạn return result;, trình biên dịch là buộc thử return std::move(result); đầu tiên.

bạn có thể mất, vì return std::move(result); đặc biệt ngăn ngừa RVO nếu đó là trường hợp áp dụng.

+0

Trên thực tế, trong trường hợp RVO là không thể, và nếu kiểu trả về có thể di chuyển và có bộ đệm cơ bản lớn, bạn có thể đạt được rất nhiều. Đây cũng không phải là câu trả lời cho câu hỏi. – galinette

+2

@galinette Không, nếu không thể thực hiện việc sao chép bản sao vì bất kỳ lý do nào, 'kết quả trả về;' sẽ vẫn thực hiện di chuyển khi có thể. –

+0

@galinette Vâng, nếu RVO là không thể, sau đó 'result' không phải là một biến địa phương cũng không phải là một tham số của hàm, do đó, bằng cách sử dụng' std :: move' không phải là không có trí tuệ trong trường hợp này anyway. Bạn đã hỏi về RVO, vì vậy tôi đã giả định một tình huống mà các tiêu chí RVO được đáp ứng. – Angew

Các vấn đề liên quan