2014-09-24 40 views
11

Hơi ngạc nhiên (với tôi), hai chương trình sau biên dịch để đầu ra khác nhau, với một thứ hai có hiệu suất tốt hơn (thử nghiệm với gcc và kêu vang):tự động tối ưu hóa Xvalue

#include <vector> 
int main() 
{ 
    std::vector<int> a(2<<20); 
    for(std::size_t i = 0; i != 1000; ++i) { 
     std::vector<int> b(2<<20); 
     a = b; 
    } 
} 

vs

#include <vector> 
int main() 
{ 
    std::vector<int> a(2<<20); 
    for(std::size_t i = 0; i != 1000; ++i) { 
     std::vector<int> b(2<<20); 
     a = std::move(b); 
    } 
} 

có thể ai đó giải thích cho tôi lý do tại sao các trình biên dịch không (hoặc có thể) không tự động xem xét b một Xvalue trong việc bố trí cuối cùng và áp dụng ngữ nghĩa di chuyển mà không có sự rõ ràng std::move dàn diễn viên?

Sửa: Biên soạn với (g++|clang++) -std=c++11 -O3 -o test test.cpp

+0

Các tham số bạn đang chuyển đến trình biên dịch là gì? – Joe

+1

Đoán đầu tiên của tôi là điều này sẽ thay đổi ngữ nghĩa của chương trình theo cách bất ngờ chuyển bản sao thành một động tác. – pmr

+0

@pmr: Đó cũng là điều tôi nghi ngờ, nhưng tôi thực sự muốn hiểu tại sao. Naively, nó có vẻ chính xác như những gì một xvalue nên được cho tôi. – Xoph

Trả lời

6

Compilers can't break the as-if rule

Như §1.9/1 trạng thái:

Các mô tả ngữ nghĩa trong tiêu chuẩn này định nghĩa một tham số máy trừu tượng không xác định . Số điện thoại quốc tế tiêu chuẩn này không yêu cầu cấu trúc phù hợp với việc triển khai . Đặc biệt, họ không cần sao chép hoặc mô phỏng cấu trúc của máy trừu tượng. Thay vào đó, việc triển khai phù hợp được yêu cầu phải thi đua (chỉ) các hành vi quan sát của máy trừu tượng như được giải thích dưới đây

tức là một trình biên dịch không thể thay đổi hành vi quan sát của chương trình. Tự động (ngay cả khi không có hậu quả) chuyển đổi một nhiệm vụ cho một nhiệm vụ di chuyển sẽ phá vỡ tuyên bố này.

Các bản sao có thể thay đổi một chút hành vi này, nhưng điều đó được quy định bởi §12.8/31.

Nếu bạn muốn sử dụng phiên bản di chuyển, bạn sẽ phải yêu cầu một cách rõ ràng như trong ví dụ sau.

+1

OK, do đó, cụ thể là các lập trình viên nên có đáng tin cậy sao chép/di chuyển nhà điều hành/ctor gọi. Tôi bằng cách nào đó giả định nó sẽ là hợp lý để yêu cầu các lập trình viên để làm cho các hoạt động tương thích ngữ nghĩa. Tôi đoán cả hai cách tiếp cận sẽ có ưu và khuyết điểm của họ, nhưng tôi có thể thấy tại sao tiêu chuẩn lại nhìn thấy nó khác đi. – Xoph

+2

Trong mã cụ thể này, nó sẽ không phá vỡ quy tắc as-if vì không có đầu ra –

5

Hãy nhìn vào các mẫu tiếp theo (xin vui lòng bỏ qua void kiểu trả về từ operator=):

#include <iostream> 

struct helper 
{ 
    void operator=(helper&&){std::cout<<"move"<<std::endl;} 
    void operator=(const helper&){std::cout<<"copy"<<std::endl;} 
}; 

void fun() 
{ 
    helper a; 
    { 
     helper b; 
     a = b; 
    } 
} 

void gun() 
{ 
    helper a; 
    { 
     helper b; 
     a = std::move(b); 
    } 
} 
int main() 
{ 
    fun(); 
    gun(); 
} 

Các operator= có hành vi khác nhau tùy thuộc vào đối số của nó. Trình biên dịch chỉ được phép tối ưu hóa mã nếu nó có thể duy trì hoạt động quan sát giống nhau.

Xét b từ fun một xvalue, trong khi nó không phải là một xvalue tại thời điểm của cuộc gọi nó sẽ làm thay đổi hành vi quan sát của chương trình và đây không phải là mong muốn và cũng không cho phép theo tiêu chuẩn.

+0

Cảm ơn, tôi đã nhận thức được sự thay đổi trong cách gọi hàm tạo. Tôi cho rằng lập trình viên bằng cách nào đó được yêu cầu để tạo các nhà thầu/nhà khai thác di chuyển và sao chép các nhà xây dựng/nhà khai thác "phù hợp ngữ nghĩa", và tôi đoán đó là lỗi của tôi ở đây! – Xoph