2010-11-10 66 views
6

請看,我沒有得到的是,爲什麼像下面這樣的程序是合法的?在C++中沒有副作用的表達式

int main() 
{ 
    static const int i = 0; 
    i <i> i; 
} 

我的意思,當然,沒有人實際上有有沒有副作用在其中表達的任何現有計劃,因爲那將是非常沒有意義的,它將使解析&編譯語言要容易得多。那麼爲什麼不禁止他們呢?這種語法實際上從這種語法中獲得了什麼好處?

另一個例子是這樣的:

int main() { 
    static const int i = 0; 
    int x = (i); 
} 

什麼是這樣陳述的實際好處?

而最令人頭疼的解析。有沒有人在其他函數中聲明函數?我的意思是,我們擺脫了隱式函數聲明等事情。爲什麼不只是擺脫他們的C++ 0x?

回答

4

它將使解析&編譯 語言更容易

我怎麼沒看到。爲什麼分析和編譯i <i> i更容易,如果你需要需要來發布診斷,而不是解析它,如果你被允許做任何你該死的任務,請確保發出的代碼沒有副作用?

Java編譯器禁止無法訪問的代碼(與無效的代碼相反),這對程序員來說是一種混合的祝福,並且需要編譯器的額外工作,而不是C++編譯器實際需要做的事情(基本塊依賴性分析)。 C++應該禁止無法訪問的代碼?可能不會。儘管C++編譯器確實做了足夠的優化來識別無法訪問的基本塊,但在某些情況下,它們可能做得太多。如果foo是一個錯誤的編譯時常量,if (foo) { ...}應該是一個非法的不可達塊嗎?如果它不是一個編譯時常量,但是優化器已經計算出如何計算該值,如果它是合法的並且編譯器必須認識到它將它移除的原因是特定於實現的,以免給出錯誤?更特殊的情況。

沒有人真正有任何電流 程序,有沒有 副作用他們

載荷表達式。例如,如果NDEBUG爲真,那麼assert將擴展爲無效表達式,但不起作用。所以在編譯器中需要更多特殊情況來允許一些無用的表達式,但不允許使用其他表達式。 (a)編譯器最終會拋出諸如if (foo) assert(bar);之類的警告,並且(b)這樣的代碼在發佈時是合法的,但在調試時不合法,只是令人困惑:

assert(foo) // oops, forgot the semi-colon 
foo.bar(); 

之類的東西最讓人頭疼的解析

,這就是爲什麼它被稱爲 「令人煩惱」。這是一個向後兼容的問題。如果C++現在改變了那些煩人的分析的含義,現有代碼的含義就會改變。正如你指出的那樣,現有的代碼並不多,但C++委員會在向後兼容性方面採取了相當強硬的措施。如果您想要每五分鐘更改一次的語言,請使用Perl ;-)

無論如何,現在已經太晚了。即使我們對C++ 0x委員會錯過了一些很好的見解,爲什麼某些特性應該被刪除或者不兼容地改變,除非FCD明確錯誤,否則它們不會破壞FCD中的任何內容。

請注意,對於您的所有建議,任何編譯器都可能爲它們發出警告(實際上,我不明白您的問題與第二個示例有什麼關係,但對於無用的表達式和對函數體中令人厭煩的解析來說肯定是這樣) 。如果你是對的,沒有人故意這樣做,警告不會造成任何傷害。如果你錯誤地認爲沒有人這麼做,你所說的刪除它們的情況是不正確的。流行編譯器中的警告可能會爲刪除功能鋪平道路,特別是因爲標準主要由編譯器編寫者撰寫。事實上,我們並不總是得到這些事情的警告,這對我來說意味着它比你想象的還要多。

8

可能是因爲禁止會使規範更復雜,這會使編譯器更復雜。

+0

除了如果編譯器有兩種方法可以解析語句,但其中一種是非法的,那麼它知道它必須是另一種,這很簡單,而如果標準允許這兩種方式,那麼編譯器必須決定哪一個,哪一個簡單得多。那裏的可行性程序越不可靠,您需要決定哪些程序的邏輯就越少。 – Puppy 2010-11-10 12:43:10

+0

雖然這並非總是如此,因爲編譯器需要檢測某些無效表達式並報告適當的錯誤消息。在任何情況下,如你所暗示的,它肯定不能認爲代碼是有效的。 – 2010-11-10 16:02:36

+0

+1表示智能(和有效)參數。 :-) – 2011-01-04 22:09:43

0

爲什麼不應該將其視爲一種特殊情況?此外,雖然上述情況很容易發現,但人們可以想象更加複雜的程序,其中不容易識別沒有副作用。

0

作爲C++標準的一個迭代,C++ 0x必須向後兼容。沒有人可以斷言你寫的這些陳述並不存在於美國國家航空航天局或國防部編寫的一些關鍵軟件中。

反正關於你的第一個例子中,解析器不能斷言i是一個靜態常量表達式,而i <i> i是一個無用的表達 - 例如如果i是模板類型,則i <i> i是「無效變量聲明」,而不是「無用計算」,但仍不是分析錯誤。

+1

沒有人說NASA或DoD必須升級到C++ 0x。如果他們喜歡它,那麼他們可以保留C++ 03。舊的編譯器版本不會奇蹟般地消失。 – Puppy 2010-11-10 12:51:19

0

也許操作員被超載有副作用,如cout<<i;這就是爲什麼他們現在不能被刪除的原因。另一方面,C#禁止非賦值語句或方法調用表達式作爲語句,我相信這是一件好事,因爲它使代碼更清晰,語義更正確。但是C#有機會從C++沒有的最開始就禁止它。

3
  • 有時候會將無用的語句放入程序並編譯它以確保它們是合法的 - 例如,涉及的類型可以被解析/匹配等。

  • 特別是在生成的代碼(宏以及更復雜的外部機制,策略或類型可能會在某些非操作情況下引入無意義的擴展的模板)不可編譯的案例,以避免讓事情變得更簡單

  • 可能會有一些臨時註釋的代碼去除變量的有意義的用法,但是可能很痛苦的是必須類似地識別和評論其他地方未使用的所有變量。

  • 雖然在您的示例中顯示的變量是「int」,緊挨在無意義的用法之上,但實際上類型可能更加複雜(例如,操作符<())以及操作是否具有副作用甚至可能是編譯器所不知道的(例如,外聯函數),所以任何好處都侷限於更簡單的情況。

  • C++需要一個很好的理由來反轉(和保留C)兼容性。

0

沒有副作用的表達式可能比您在模板化和宏代碼中的想法更頻繁。如果你曾經聲明std::vector<int>,你已經實例化模板代碼,沒有副作用。 std::vector在釋放自身時必須銷燬所有元素,以防存儲類型爲T的類。這需要在某種程度上類似於ptr->~T();來調用析構函數。 int雖然沒有析構函數,所以調用沒有副作用,並且會被優化器完全刪除。它也可能在一個循環內,然後整個循環沒有副作用,所以整個循環被優化器刪除。

所以,如果你不允許的,無副作用的表達,std::vector<int>是行不通的,換一個。

另一種常見情況是assert(a == b)。在發佈版本中,您希望這些斷言消失 - 但不能將其重新定義爲空宏,否則像if (x) assert(a == b);這樣的語句會突然將下一個語句放入if語句中 - 這是一種災難!在這種情況下,assert(x)可以重新定義爲((void)0),這是一個沒有副作用的聲明。現在,if語句在發佈版本中也能正確運行 - 它只是無所作爲。

這些只是兩個常見的情況。還有很多你可能不知道的。所以,雖然沒有副作用的表達看起來多餘,但它們實際上在功能上很重要。優化器將完全移除它們,所以也不會影響性能。

相關問題