重新審視未定義行為:序列點和用戶定義類型
問題:
在上一篇關於未定義行為和序列點的文章中,表達式i = i 被認為是呼叫內建類型的未定義行為。然而,問題來了:如果 i 的類型是使用者定義的怎麼辦?具體來說,假設我們有一個帶有重載運算子的 Index 類別。
答案:未定義的行為與明確定義的行為
與直覺相反,事實證明,表達式i = i 不會調用Index 等用戶定義類型的未定義行為。這是因為 Index 中的重載運算子被視為函數。根據 C ISO 標準,函數求值在函數參數求值後引入一個序列點,複製回傳值後再引入另一個序列點。
序列點和重載運算子
在 i = i 的情況下,表達式 i 在作為參數傳遞給運算子 = 之前先進行計算。這表示在對 i 求值之後存在一個序列點,確保物件 i 在連續的序列點之間僅被修改一次。
因此,表達式i = i 對於使用者定義類型(如Index)是等效的編寫i.operator =(i.operator ()); ,這是一個定義良好的表達式,沒有未定義的行為。這同樣適用於語法上較簡單的表達式 i.add(i.inc());。
微妙的區別:
重要的是要注意 i = i 不是 C 語法意義上的表達式。相反,它是一個“語句表達式”,它是表達式和語句的語法組合。語句表達式與普通語句一樣執行,但其結果也可以賦值給變數。
a[ i] = i
如果a 是陣列對於內建類型,表達式a[ i] = i 會呼叫未定義的行為,因為左值a[ i] 被求值兩次:一次在表達式i 中,另一次在賦值i = 中i.
但是,如果 a是重載下標運算子的使用者定義類型,則表達式的行為可能會有所不同,具體取決於下標運算子的實作。例如,如果使用 const Index& operator[](Index i) 實作下標運算符,則表達式將是明確定義的,因為在呼叫下標運算子之前,左值 a[ i] 僅計算一次。
i 的有效性;
表達式 i;在 C 03 中定義良好,因為它等價於 ((i.operator ()).operator ()).operator ()。這是因為每個運算子引入了一個序列點,確保物件 i 在連續的序列點之間僅被修改一次。
以上是`i = i` 是否會在 C 中呼叫使用者定義類型的未定義行為?的詳細內容。更多資訊請關注PHP中文網其他相關文章!