10

The C++11 standard (5,17, expr.ass) cho rằngphân đôi của cùng một biến trong một biểu thức trong C++ 11

Trong mọi trường hợp, việc chuyển nhượng được lập trình tự sau khi tính toán giá trị quyền và toán hạng bên trái và trước phép tính giá trị của biểu thức gán. đối với một indeterminately-trình tự gọi hàm với, hoạt động của một nhiệm vụ phức hợp là một đánh giá đơn

Theo tôi được biết, tất cả các biểu thức mà là một phần của công việc nhất định sẽ được đánh giá trước nhiệm vụ riêng của mình . Quy tắc này sẽ hoạt động ngay cả khi tôi sửa đổi cùng một biến hai lần trong cùng một nhiệm vụ, mà tôi khá chắc chắn, là hành vi không xác định trước đó.

sẽ mã đưa ra:

int a = 0; 
a = (a+=1) = 10; 

if (a == 10) { 
    printf("this is defined"); 
} else { 
    printf("undefined"); 
} 

luôn luôn đánh giá a==10?

+0

Lưu ý phụ: Kiểm tra kết quả của biểu thức có thể không xác định không cho chúng tôi biết đó có thực sự là UB hay không. Nó có thể là UB và tạo ra kết quả chính xác. – jrok

+3

@jrok đó là mã mẫu, có lẽ ngay cả một SSCCE, vì SO yêu cầu tôi phải * bao gồm mã hợp lệ *. Tôi đã thử kiểm tra nó, chỉ vì tò mò, nhưng tôi nhận ra nó không chứng minh gì; do đó tôi thậm chí không đề cập đến nó. – Dariusz

+2

@jrok - nó có thể tạo ra kết quả "chính xác". Các dấu ngoặc kép rất quan trọng. '' –

Trả lời

6

Hãy viết lại mã của bạn như

E1 = (E2 = E3) 

nơi E1 là sự biểu hiện a, E2 là sự biểu hiện a += 1 và E3 là sự biểu hiện 10. Ở đây chúng ta đã sử dụng, toán tử gán nhóm từ phải sang trái (§5.17/1 trong C++ 11 Standard).

§5.17/1 hơn nữa khẳng định:

Trong mọi trường hợp, việc chuyển nhượng được lập trình tự sau khi tính toán giá trị của quyền và trái toán hạng, và trước khi tính toán giá trị của biểu thức chuyển nhượng.

Áp dụng điều này vào biểu thức của chúng tôi có nghĩa là trước tiên chúng tôi phải đánh giá các biểu thức con E1E2 = E3. Lưu ý rằng không có mối quan hệ "được sắp xếp trước" giữa hai đánh giá này, nhưng điều đó không gây ra vấn đề gì.

Việc đánh giá biểu thức id E1 là không đáng kể (kết quả là a chính nó). Việc đánh giá biểu thức chuyển nhượng biểu thức chuyển nhượngE2 = E3 như sau:

Đầu tiên cả hai biểu thức con phải được đánh giá. Việc đánh giá nghĩa đenE3 là một lần nữa tầm thường (cung cấp giá trị gia tăng của giá trị 10).

Việc thẩm định (hợp chất) nhượng thể hiệnE2 được thực hiện theo các bước sau:

1) Hành vi của a += 1 tương đương với a = a + 1 nhưng a chỉ được đánh giá một lần (§5.17/7) . Sau khi đánh giá các biểu thức con a1 (theo thứ tự tùy ý), một chuyển đổi lvalue-to-rvalue được áp dụng cho a để đọc giá trị được lưu trữ trong a.

2) Các giá trị của a (đó là 0) và 1 được thêm vào (a + 1) và kết quả bổ sung này là một prvalue giá trị 1.

3) Trước khi chúng tôi có thể tính toán kết quả của nhiệm vụ a = a + 1 giá trị của đối tượng toán hạng bên trái đề cập đến được thay thế bằng giá trị của toán hạng bên phải (§5.17/2). Kết quả của E2 sau đó là một định giá trị bằng lvalue cho giá trị mới 1.Lưu ý rằng tác dụng phụ (cập nhật giá trị của toán hạng bên trái) được giải trình tự trước khi tính toán giá trị của biểu thức gán. Đây là §5.17/1 được trích dẫn ở trên.

Bây giờ chúng ta đã đánh giá subexpressions E2E3, giá trị của biểu thức E2 đề cập đến được thay thế bằng giá trị của E3, đó là 10. Do đó kết quả của E2 = E3 là một giá trị của giá trị 10.

Cuối cùng, biểu thức giá trị E1 đề cập đến được thay thế bằng giá trị của biểu thức E2 = E3, mà chúng tôi đã tính là 10. Do đó, biến số a kết thúc để chứa giá trị 10.

Vì tất cả các bước này được xác định rõ, toàn bộ biểu thức mang lại giá trị được xác định rõ.

7

Có, đã có sự thay đổi giữa C++ 98 và C++ 11. Tôi tin rằng ví dụ của bạn được xác định rõ ràng theo quy tắc C++ 11, trong khi trưng bày hành vi không xác định theo quy tắc C++ 98.

Ví dụ đơn giản hơn, x = ++x; không được xác định trong C++ 98 nhưng được xác định rõ trong C++ 11. Lưu ý rằng x = x++; vẫn chưa được xác định (tác dụng phụ của post-increment là không có kết quả với việc đánh giá biểu thức, trong khi tác dụng phụ của pre-increment được sắp xếp trước khi giống nhau).

+0

Đó là điều thú vị và tốt để biết, mặc dù bạn không may không giải quyết mã được đăng. Bạn có bất kỳ suy nghĩ về nó là hành vi không xác định/không xác định? – Dariusz

+0

Tôi tin rằng ví dụ của bạn được xác định rõ. Đã cập nhật câu trả lời cho phù hợp. –

3

Sau khi thực hiện một nghiên cứu nhỏ, tôi tin chắc rằng hành vi mã của bạn được xác định rõ trong C++ 11.

$ 1,9/15 trạng thái:

Các tính toán giá trị của toán hạng của một nhà điều hành được sắp xếp trước việc tính toán giá trị của các kết quả của các nhà điều hành.

$ 5,17/1 trạng thái:

Toán tử gán (=) và các hợp chất toán tử gán tất cả các nhóm từ phải sang trái.

Nếu tôi hiểu đúng, trong ví dụ của bạn

a = (a+=1) = 10; 

này ngụ ý rằng các tính toán giá trị của (a+=1)10 phải được thực hiện trước khi tính toán giá trị của (a+=1) = 10 và tính toán giá trị của biểu thức này phải được hoàn thành trước khi a = (a+=1) = 10; được đánh giá.

$ 5,17/1 trạng thái:

Trong mọi trường hợp, việc chuyển nhượng được lập trình tự sau khi tính toán giá trị của quyền và trái toán hạng, và trước khi tính toán giá trị của biểu thức chuyển nhượng.

này ngụ ý rằng việc chuyển nhượng phải xảy ra trước khi tính toán giá trị, và do đó, do transitivity, việc thẩm định (a+=1) = 10 chỉ có thể bắt đầu sau khi chuyển nhượng a+=1 (Bởi vì giá trị của nó chỉ có thể được tính toán sau khi tác dụng phụ).

Điều tương tự cũng đúng đối với nhiệm vụ thứ hai và thứ ba.

Xem thêm điều này excellent answer, giải thích mối quan hệ được sắp xếp trước đó trong nhiều chi tiết hơn và cách tốt hơn tôi có thể.

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