Tôi đã đọc về thứ tự vi phạm đánh giá , và họ đưa ra một ví dụ đánh đố tôi.
1) Nếu một hiệu ứng phụ trên một đối tượng vô hướng không được giải trình tự so với hiệu ứng phụ khác trên cùng một đối tượng vô hướng, thì hành vi không được xác định.
// snip f(i = -1, i = -1); // undefined behavior
Trong bối cảnh này, i
là một đối tượng vô hướng , có nghĩa là rõ ràng
Các loại số học (3.9.1), loại liệt kê, loại con trỏ, con trỏ đến loại thành viên (3.9.2), std :: nullptr_t và các phiên bản đủ điều kiện cv của các loại này (3.9.3) được gọi chung là các loại vô hướng.
Tôi không thấy cách tuyên bố mơ hồ trong trường hợp đó. Đối với tôi, dường như bất kể đối số thứ nhất hay thứ hai được đánh giá đầu tiên, i
kết thúc là -1
và cả hai đối số cũng vậy -1
.
Ai đó có thể vui lòng làm rõ?
CẬP NHẬT
Tôi thực sự đánh giá cao tất cả các cuộc thảo luận. Cho đến nay, tôi thích câu trả lời của @ Harmic rất nhiều vì nó phơi bày những cạm bẫy và rắc rối của việc xác định tuyên bố này mặc dù nó nhìn thẳng về phía trước như thế nào. @ acheong87 chỉ ra một số vấn đề xuất hiện khi sử dụng tài liệu tham khảo, nhưng tôi nghĩ đó là trực giao với khía cạnh tác dụng phụ chưa được giải đáp của câu hỏi này.
TÓM LƯỢC
Vì câu hỏi này có rất nhiều sự chú ý, tôi sẽ tóm tắt những điểm / câu trả lời chính. Đầu tiên, cho phép tôi giải thích nhỏ để chỉ ra rằng "tại sao" có thể có ý nghĩa khác nhau nhưng có ý nghĩa khác nhau, cụ thể là "vì lý do gì ", "vì lý do gì " và "vì mục đích gì ". Tôi sẽ nhóm các câu trả lời theo nghĩa nào trong số những ý nghĩa của "tại sao" mà chúng giải quyết.
vì lý do gì
Câu trả lời chính ở đây đến từ Paul Draper , với Martin J đóng góp một câu trả lời tương tự nhưng không rộng rãi. Câu trả lời của Paul Draper sôi sùng sục
Đó là hành vi không xác định bởi vì nó không được xác định hành vi là gì.
Câu trả lời là rất tốt về mặt giải thích những gì tiêu chuẩn C ++ nói. Nó cũng giải quyết một số trường hợp liên quan của UB như f(++i, ++i);
và f(i=1, i=-1);
. Trong trường hợp đầu tiên của các trường hợp liên quan, không rõ liệu đối số thứ nhất nên là i+1
và đối số thứ hai i+2
hay ngược lại; trong lần thứ hai, không rõ i
là 1 hay -1 sau khi gọi hàm. Cả hai trường hợp này đều là UB vì chúng thuộc quy tắc sau:
Nếu một tác dụng phụ trên một đối tượng vô hướng không bị ảnh hưởng so với tác dụng phụ khác trên cùng một đối tượng vô hướng, thì hành vi không được xác định.
Do đó, f(i=-1, i=-1)
cũng là UB vì nó thuộc cùng một quy tắc, mặc dù ý định của lập trình viên là (IMHO) rõ ràng và không mơ hồ.
Paul Draper cũng làm cho nó rõ ràng trong kết luận của mình rằng
Nó có thể đã được xác định hành vi? Đúng. Nó đã được định nghĩa? Không.
điều này đưa chúng ta đến câu hỏi "vì lý do / mục đích nào còn f(i=-1, i=-1)
lại là hành vi không xác định?"
vì lý do / mục đích gì
Mặc dù có một số lỗi quá mức (có thể bất cẩn) trong tiêu chuẩn C ++, nhiều thiếu sót được suy luận hợp lý và phục vụ một mục đích cụ thể. Mặc dù tôi biết rằng mục đích thường là "làm cho công việc của trình biên dịch dễ dàng hơn" hoặc "mã nhanh hơn", nhưng tôi chủ yếu quan tâm để biết liệu có lý do chính đáng nào để lại f(i=-1, i=-1)
là UB không.
harmic và supercat cung cấp câu trả lời chính mà cung cấp một lý do cho UB. Harmic chỉ ra rằng một trình biên dịch tối ưu hóa có thể phá vỡ các hoạt động gán nguyên tử rõ ràng thành nhiều lệnh máy và nó có thể xen kẽ các hướng dẫn đó để có tốc độ tối ưu. Điều này có thể dẫn đến một số kết quả rất đáng ngạc nhiên: i
kết thúc là -2 trong kịch bản của anh ấy! Do đó, tác hại cho thấy cách gán cùng một giá trị cho một biến nhiều lần có thể có tác động xấu nếu các hoạt động không được thực hiện.
supercat cung cấp một giải trình có liên quan về những cạm bẫy của việc cố gắng f(i=-1, i=-1)
làm những gì có vẻ như nó phải làm. Ông chỉ ra rằng trên một số kiến trúc, có những hạn chế cứng đối với nhiều lần ghi đồng thời vào cùng một địa chỉ bộ nhớ. Một trình biên dịch có thể gặp khó khăn trong việc nắm bắt điều này nếu chúng ta đang xử lý một cái gì đó tầm thường hơn f(i=-1, i=-1)
.
davidf cũng cung cấp một ví dụ về các hướng dẫn xen kẽ rất giống với các tác hại.
Mặc dù mỗi ví dụ của các tác nhân gây hại, supercat và davidf 'có phần bị chiếm đoạt, chúng được kết hợp với nhau để cung cấp một lý do hữu hình tại sao f(i=-1, i=-1)
nên không xác định hành vi.
Tôi chấp nhận câu trả lời có hại vì nó đã làm tốt nhất việc giải quyết tất cả các ý nghĩa tại sao, mặc dù câu trả lời của Paul Draper đã giải quyết phần "vì lý do gì" tốt hơn.
câu trả lời khác
JohnB chỉ ra rằng nếu chúng ta xem xét các toán tử gán quá tải (thay vì chỉ là vô hướng đơn giản), thì chúng ta cũng có thể gặp rắc rối.
f(i-1, i = -1)
hoặc một cái gì đó tương tự.
std::nullptr_t
và các phiên bản đủ điều kiện cv của các loại này (3.9.3) được gọi chung là các loại vô hướng . "