2015-10-30 24 views
10

Xét đoạn mã sau trong C++:Không trả về một đối tượng tạm thời tạo một đối tượng tạm thời trong C++?

struct A {A(int);}; 
A foo() {return static_cast<A>(0);} 
A x = foo(); 

Đây static_cast<A>(0) tạo ra một đối tượng tạm thời bởi các tiêu chuẩn [5.2.9-4], mà là một prvalue. Các tiêu chuẩn [12,2-1] nói

temporaries của kiểu lớp được tạo ra trong những bối cảnh khác nhau: ràng buộc một tham chiếu đến một prvalue (8.5.3), trả lại một prvalue (6.6.3), một chuyển đổi tạo ra một giá trị (4.1, 5.2.9, 5.2.11, 5.4), ném một ngoại lệ (15.1), nhập một trình xử lý (15.3), và trong một số lần khởi tạo (8.5).

Vì vậy, câu lệnh trả về sẽ tạo lại đối tượng tạm thời?

Nhân tiện, bất cứ ai có thể vui lòng cho tôi biết liệu tiêu chuẩn đảm bảo chuyển đổi loại tiềm ẩn sẽ tạo ra một đối tượng tạm thời không?

+2

Điều này sẽ là RVO'd – SingerOfTheFall

+1

https://en.wikipedia.org/wiki/Return_value_optimization – LBes

+0

Nó được phép tạo tạm thời, nhưng có thể sẽ không được. Xem [Cách tốt nhất để trả về một lớp trong C++] (http://stackoverflow.com/questions/12505563/best-way-to-return-a-class-in-c) –

Trả lời

7

(§4/6) đề cập rằng

Hiệu quả của bất kỳ chuyển đổi ngầm định nào cũng giống như thực hiện khai báo và khởi tạo tương ứng và sau đó sử dụng biến tạm thời là kết quả của chuyển đổi.

Vì vậy, có, trừ khi được tối ưu hóa, tạm thời nên được tạo, nhưng tôi chắc chắn tất cả trình biên dịch hiện đại sẽ thực hiện một bản sao trong trường hợp của bạn. Tối ưu hóa cụ thể này được gọi là return value optimization (RVO). Bạn easilly có thể kiểm tra nó bằng cách có nhà xây dựng với tác dụng phụ:

struct A { 
    A(int){ 
     std::cout << "ctor"; 
    } 
    A(const A & other) 
    { 
     std::cout << "copy ctor"; 
    } 
    A(A&&other) 
    { 
     std::cout << "move ctor"; 
    } 
}; 
+0

"nhưng tôi chắc chắn tất cả các trình biên dịch hiện đại sẽ thực hiện một bản sao" - Họ có thể, nhưng điều đó không nhất thiết có nghĩa là họ sẽ. GCC có tùy chọn dòng lệnh '-fno-elide-constructors' để vô hiệu hóa việc tách bản sao, chẳng hạn. Ngoài ra, sao chép elision là tùy chọn vì trong trường hợp chung, nó có thể không được biết tại thời điểm một đối tượng được xây dựng, cho dù nó sẽ được sử dụng như là giá trị trả về. Sẽ luôn có những trường hợp tiêu chuẩn cho phép xóa bỏ bản sao, nhưng việc triển khai sẽ không thực hiện nó. (Nhưng đối với mã cụ thể của OP tôi cũng sẽ ngạc nhiên nếu bất kỳ trình biên dịch hiện đại không thể áp dụng RVO.) – hvd

+0

các tác dụng phụ của các nhà xây dựng không gây RVO không được thực hiện, xem [câu trả lời của tôi] (http://stackoverflow.com/a/33433512/678093) –

+0

Tôi biết sao chép elision, và tôi chỉ muốn biết các trường hợp mà không có bản sao elision. Tôi xin lỗi vì đã quên đề cập đến vấn đề này. Cảm ơn câu trả lời của bạn. – xskxzr

2

Kết quả thực tế trong trường hợp cụ thể này sẽ phụ thuộc vào một trình biên dịch và mức tối ưu cụ thể. Trong thực tế, một trình biên dịch hiện đại, phong nha với một mức tối ưu hóa tốt hoàn toàn có thể loại bỏ bất kỳ đối tượng tạm thời nào. Xem xét việc này:

#include <iostream> 

using namespace std; 

struct A { 
    A(int) { cout << __PRETTY_FUNCTION__ << endl; } 
    ~A() { cout << __PRETTY_FUNCTION__ << endl; } 
}; 

inline 
A foo() { 
    return static_cast<A>(0); 
}; 


int main(void) { 
    A a = foo(); 
    cout << "hello world!" << endl; 
} 

gcc-5.1.1 với -O4 xây dựng một thực thi mà kết quả đầu ra theo nghĩa đen này:

A::A(int) 
hello world! 
A::~A() 
+0

Hoặc thậm chí với '-O3', vì '-O4' không thay đổi gì ngoài điều đó. :) – davmac

+1

Hoặc thậm chí không có bất kỳ tùy chọn '-O' nào, vì việc sao chép bản sao không bị ảnh hưởng bởi các tùy chọn đó. – hvd

5

Đối tượng tạm thời sẽ (rất có thể) được tối ưu hóa đi qua Return-Value-Optimization (RVO).

Ví dụ:

#include <iostream> 
struct A 
{ 
    A(int) 
    { 
     std::cout<< "A" << std::endl; 
    } 
    A(const A&) 
    { 
     std::cout << "A&" << std::endl; 
    } 
    A(A&&) 
    { 
     std::cout << "A&&" << std::endl; 
    } 
}; 
A foo() {return static_cast<A>(0);} 

int main() 
{ 
    A x = foo(); 
    return 0; 
} 

đầu ra: live example

A 

đầu ra với RVO khuyết tật: live example

A 
A&& 
A&& 
4

Câu trả lời ngắn: Không có sẽ chỉ có một sự sáng tạo của A trong mã của bạn.

Để đạt được điều này, trình biên dịch sử dụng (Named) Return value optimization để loại bỏ việc tạo/sao chép đối tượng không cần thiết khi trả về. Trường hợp tổng quát hơn, Copy elision, loại bỏ việc sao chép các đối tượng không cần thiết, sẽ được sử dụng trong nhiều trường hợp liên quan.

Bạn có thể chơi với tùy chọn GCC-fno-elide-constructors để xem sự khác biệt.

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