2014-09-16 14 views
28

Tôi biết rằng trong tình hình sau đó trình biên dịch được tự do di chuyển-xây dựng các giá trị trả về từ makeA (nhưng cũng là miễn phí để bõ mẫu âm chót sao chép hoặc di chuyển hoàn toàn):Có thể trả về một biến cục bộ theo giá trị trong C++ 11/14 dẫn đến giá trị trả về đang được xây dựng bởi rvalue khi không có copy/move được tham gia?

struct A 
{ 
    A(A&); 
    A(A&&); 
}; 

A makeA() 
{ 
    A localA; 
    return localA; 
} 

Những gì tôi tự hỏi là liệu trình biên dịch được phép xây dựng một đối tượng kiểu A từ một đối tượng địa phương loại B bằng tham chiếu rvalue nếu nó đang được xây dựng trong câu lệnh trả về. Nói cách khác, trong ví dụ sau, trình biên dịch có được phép chọn phương thức khởi tạocho giá trị trả về không?

struct B { }; 
struct A { 
    A(A&); // (1) 
    A(A&&); // (2) 
    A(B&); // (3) 
    A(B&&); // (4) 
}; 

A makeA() 
{ 
    B localB; 
    return localB; 
} 

Tôi yêu cầu này vì nó sẽ có vẻ với tôi rằng cùng một logic cho phép một đối tượng địa phương của loại A được đối xử như một rvalue trong câu lệnh return cũng nên cho phép một địa phương của bất kỳ loại được đối xử như một giá trị, nhưng tôi không thể tìm thấy bất kỳ ví dụ hoặc câu hỏi nào về bản chất này.

+1

Kiểm tra nhanh trên clang và g ++ cho thấy cả hai cuộc gọi # 3. –

+0

'localB' không phải là một rvalue, vì vậy không (tốt, nó có thể làm bất cứ điều gì nó muốn miễn là nó tuân theo quy tắc" as-if ", nhưng nó không có khả năng sử dụng một di chuyển ở đây.) Bạn sẽ cần' std: : di chuyển (localB) '. – juanchopanza

+0

@juanchopanza Không tin rằng đây là một bản sao - nếu bạn 'trả về localA;' và ngăn chặn RVO, bạn sẽ chọn hàm tạo đang dùng một 'A &&'. Câu hỏi là cụ thể cho báo cáo trả về. –

Trả lời

30

Quy tắc cho tình huống này đã thay đổi từ năm 2011 đến năm 2014. Trình biên dịch giờ đây sẽ xử lý localB làm giá trị.

Nguyên tắc áp dụng cho return báo cáo được tìm thấy trong §12.8 [class.copy]/P32, mà đọc trong C++ 14 (trích dẫn N3936, tôi nhấn mạnh):

Khi các tiêu chuẩn cho sự bỏ bớt các một hoạt động sao chép/di chuyển được đáp ứng, nhưng không cho một ngoại lệ khai, và đối tượng được sao chép được định bởi một giá trị trái, hoặc khi biểu trong một tuyên bố trở lại là một (có thể ngoặc) id -expression có tên là đối tượng với thời lượng lưu trữ tự động được khai báo trong phần nội dung hoặc thông số khai báo tham số của hàm bao trong cùng hoặc biểu thức lambda, quá trình phân giải quá tải để chọn hàm tạo cho bản sao được thực hiện đầu tiên rvalue. Nếu độ phân giải quá tải đầu tiên không thành công hoặc không được thực hiện, hoặc nếu loại tham số đầu tiên của hàm tạo được chọn là không phải tham chiếu rvalue cho loại đối tượng (có thể cv đủ điều kiện), độ phân giải quá tải được thực hiện lại, xem xét đối tượng dưới dạng giá trị .

Điều khoản in đậm được thêm bởi CWG issue 1579, rõ ràng để yêu cầu hàm tạo chuyển đổi A::A(B&&) được gọi tại đây. Điều này được thực hiện trong GCC 5 và Clang 3.9.

Trở lại năm 2011, "thử rvalue đầu tiên" quy tắc này được gắn liền với các tiêu chuẩn cho bản sao sự bỏ bớt (trích dẫn N3337):

Khi các tiêu chuẩn cho sự bỏ bớt một hoạt động sao chép được đáp ứng hoặc sẽ đáp ứng lưu cho một thực tế là đối tượng nguồn là một tham số chức năng, và đối tượng được sao chép được chỉ định bởi một giá trị, quá tải độ phân giải để chọn hàm tạo cho bản sao được thực hiện lần đầu như thể đối tượng được chỉ định bởi một giá trị .

Vì yêu cầu sao chép yêu cầu cả hai phải có cùng loại, đoạn này không áp dụng và trình biên dịch phải sử dụng hàm tạo A::A(B&).

Lưu ý rằng khi CWG 1579 được coi là DR chống lại C++ 11, trình biên dịch nên thực hiện độ phân giải của nó ngay cả trong chế độ C++ 11.

+0

lý do tôi nhận được A (B &); và A (A &&)? Tôi biên dịch mã bằng cách sử dụng -fno-elide-constructors mà vô hiệu hoá RVO. 1) Không phải là nó đáp ứng các tiêu chí cho elision? 2) A (A &&) đến từ đâu? Cảm ơn trước – camino

+0

@camino 1) Có thể là các vấn đề về phiên bản trình biên dịch - phần 'B &&' chỉ được triển khai trong GCC trunk. 2) Quay trở lại là khởi tạo sao chép, do đó, hoạt động như 'A a = b;' - 'B' đầu tiên được chuyển thành tạm thời' A', và tạm thời đó được chuyển vào giá trị trả về. –

+0

@Mikhail Việc sử dụng của tôi trong năm 2011/2014 thay vì C++ 11/C++ 14 là cố ý. Sự thay đổi đã được thực hiện thông qua một báo cáo lỗi chống lại C++ 11; những thay đổi như vậy thường được coi là "thực tế" C++ 11 bởi các nhà biên dịch trình biên dịch. –

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