2010-06-01 43 views
12

考慮以下宏定義和調用:爲什麼參數替換在重新掃描期間不被替換?

#define x x[0] 
#define y(arg) arg 

y(x) 

這種調用擴展爲x[0](上的Visual C++ 2010測試,克++ 4.1,MCPP 2.7.2和Wave)。

爲什麼?具體來說,它爲什麼不擴大到x[0][0]

在宏替換,

替換列表中的參數...被相應的參數,替換後包含的所有宏已經擴大。在被替換前,每個參數的預處理標記完全被宏代替(C++ 03§16.3.1/ 1)。

評估的宏調用,我們採取以下步驟:

  • 的函數宏y被調用,x作爲論據的arg參數
  • 在論證的x是宏替換爲x[0]
  • 替換列表中的arg被替換爲參數的宏替換值

替換所有參數後的替換列表爲x[0]

在替換列表中的所有參數都被替換之後,所得到的預處理標記序列被重新掃描......以取代更多宏名稱(C++ 03§16.3.4/ 1)。

如果在掃描替換列表期間發現正在替換的宏的名稱...它不會被替換。此外,如果任何嵌套替換遇到被替換的宏的名稱,它不會被替換(C++ 03§16.3.4/ 2)。

列表x[0]重新掃描更換(注意宏的名稱被替換y):

  • x被識別爲類似對象的宏調用
  • x被替換通過x[0]

替換sto ps,因爲§16.3.4/ 2中的規則阻止了遞歸。重新掃描後的替換列表是x[0][0]

因爲我測試過的所有預處理器都說我錯了,所以我已經明確地誤解了一些東西。另外,這個例子是C++ 0x FCD(§16.3.5/ 5)中的一個更大的例子,它也表示預期的替換是x[0]

爲什麼x在重新掃描過程中沒有被替換?

C99和C++ 0x實際上與引用段中的C++ 03具有相同的措詞。

回答

17

我相信你已經引用了關鍵段落,你剛纔停下來太快了。 16.3.4/2(重點煤礦):

如果宏被替換該掃描 替換列表中找到該名(不包括 源文件的 預處理標記的其餘部分),它是不是 取代。此外,如果任何嵌套的 替代品遇到 被替換的宏的名稱, 它不會被替換。 這些nonreplaced 宏名預處理標記沒有 不再提供進一步 更換即使他們後來 (重新)檢查他是在該 宏名預處理標記將 否則已被替換上下文。

所以,當xy參數替代期間被替換爲x[0],它完全取代宏,這意味着它是在這一點上重新掃描,並x由遞歸規則抓獲。這意味着xx[0]不再有資格獲得進一步的替換,包括在重新掃描y(x)的部分擴展結果期間。

+0

啊哈!我忘記了'x'的替換列表在它被替換之前會被重新掃描;現在這一切都有道理。謝謝,史蒂夫。 – 2010-06-01 13:06:00