2014-12-13 63 views
5

Why can't you declare a variable inside a do while loop?中,OP詢問爲什麼do-while循環的while-condition中的聲明不在do語句的範圍中。這是非常不自然的,因爲C/C++通常遵循「在頂部聲明」模式。但是,反過來說呢 - 爲什麼不把do語句中的任何聲明的範圍擴展到while-condition。這將使do-while語句正文中的聲明範圍

int i; 
do { 
    i = get_data(); 
    // whatever you want to do with i; 
} while (i != 0); 

被縮短到

do { 
    int i = get_data(); 
    // whatever you want to do with i; 
} while (i != 0); 

其給出用於限制控制變量範圍的整齊的語法。 (注:我知道這是無效的語法 - 這是問題的關鍵,爲什麼不擴展語言以允許此語法。)

正如我在下面的註釋中注意到的,此擴展不會中斷現有的代碼,並且將在引入for-init(和while-init)範圍的時候非常引人注目。

回答

0

所以目前作爲該做而存在的循環體是一個塊:

do 
{ // begin block 
    int i = get_data(); 
    // whatever you want to do with i; 
} //end block 
while (i != 0); 

它是如何與一個塊範圍內工作,從部分3.3.3塊範圍

A name declared in a block (6.3) is local to that block; it has block scope. Its potential scope begins at its point of declaration (3.3.2) and ends at the end of its block. A variable declared at block scope is a local variable.

所以顯然i的範圍是塊,所以你需要一個規則,可以在塊範圍時創建某種特殊的操作。這實際上如何工作?這似乎是最簡單等同是將申報吊到新創建的外塊:

{ 
    int i = get_data(); // hoist declaration outside 
    do 
    { // begin block 
    // whatever you want to do with i; 
    } //end block 
    while (i != 0); 
} 

這有可能,如果所有的申報工作得很好,在身體的開始,但對於這樣一個場景:

int k = 0 ; 
do 
{ 
    int i = get_data(); 
    k++ ;  // side effect before declaration 
    int j = k ; // j it set to 1 
} 
while (i != 0); 

懸掛後:

int k = 0 ; 
{ 
    int i = get_data(); 
    int j = k ; // j is set to 0 
    do 
    { 
    k++ ; 
    } 
    while (i != 0); 
} 

另一種方法是將範圍從DO,同時開始延伸,但會打破各種記錄的受到影響的行爲。這將打破名字隱藏在塊範圍是如何工作的,從部分3.3.10名稱隱藏

A name can be hidden by an explicit declaration of that same name in a nested declarative region or derived class (10.2).

和例如從3.3.1表明這一點:

int j = 24; 
int main() { 
    int i = j, j; 
    j = 42; 
} 

mainj隱藏了全球j但如何將爲我們的特別工作,而範圍?:

int j = 24; 
do { 
    int i = j, j; 
    j = 42; 
} while(j != 0) 

將前面的內容擴展到內部j的範圍,肯定會破壞很多現有代碼,並且看到前面的示例,沒有直觀的方式來提升聲明。

我們最終可以玩這個遊戲,並找到一些可行的東西,但它看起來像很多工作只是很少的收穫,它是完全不直觀的。

+0

你在代碼示例中的含義是什麼意思?*「聲明之前的副作用」*是指什麼? – dyp 2014-12-14 00:46:45

+0

@dyp我加了後應該澄清的例子,如果不讓我知道。 – 2014-12-14 17:46:48

2

在你的提案中,while語句while (i != 0);是從內塊{ int i = get_data(); }

它不會「看」到的變量i範圍
的,所以這是行不通的。

更改語言以支持將違反其規則之一的語言。範圍確定比您顯示的問題更重要,它具有在塊之前聲明變量的簡單解決方案。

還引入了您的規則,因爲這有效的例子顯示了將打破現有代碼:

int i = 0 ; 
do { 
    int i = 1 ; 
} while (i) ; 
1

一些猜想:

的C++標準委員會是知道保守 - C++是一種複雜的語言,他們是熱衷於儘可能多地避開語言和標準庫中令人困惑的邊緣案例。

其基本原理是,如果已經有表達某種邏輯的方法,則語言變化支持表達邏輯的其他方式是不必要的,並且可能會造成混淆。

循環已經可以更安全地表示,沒有未初始化的變量是這樣的:

for(;;) { 
    int i = get_data(); 
    // whatever you want to do with i; 
    if (!i) 
    break; 
} 

所以使得說法延長在do {} while();塊變量範圍將由委員會評爲「迅速駁回不必要的,可能會增加可能引起混淆的角落案例「

除此之外,還存在終生管理和銷燬順序問題。此刻,do塊中創建的任何對象將在評估while子句之前被銷燬(按確定性順序)。如果你打算延長i的生命週期,你還會延長塊中所有對象的生命週期,從而延遲析構函數的執行嗎?如果i是一個持有對該塊中聲明的另一個變量的引用的對象呢?如果while()中的代碼依賴於i的析構函數中的副作用會怎麼樣?

例子(沒有編譯):

do { 
    object1 o1; 
    object2 i(o1); // holds reference to o1 
    object3 x; 
    ... 
} while(i.test_for_end()); // implicit scope extension. 
// what if test_for_end() depends on the destructor of x? 
2

你下面的例子將不能工作,因爲,要初始化while循環中的整數值。初始化while循環內的整數值可確保while循環找不到i值並失敗。

do { 
    int i = get_data(); 
    // whatever you want to do with i; 
} while (i != 0); 

要告訴你一個例子:

do { 
int i = i+1; 

}while(i = 0) 

會給你錯誤:

prog.cpp:8:12: error: ‘i’ was not declared in this scope 
    }while(i = 0); 

理由: 看到這個邏輯圖的幫助

C++ do...while loop

標準 - do-while循環是退出條件循環。這意味着循環的主體總是首先執行。然後,評估測試條件。如果測試條件爲TRUE,程序將再次執行循環體。除了在執行語句之後而不是之前對條件進行評估之外,保證至少執行一次語句,即使條件從未滿足。

但是,在do while循環中初始化條件語句變量可以確保條件語句變量的作用域只包含在do-while循環中,而條件語句只需要全局變量和局部變量給出範圍錯誤。

理由說明: 爲什麼我們不應該改變:

不要做一個比較有效的程序是有益的,這是非常無用何況這種編碼方式是不好的,因爲你與範圍瞎搞。假設你在do-while循環中初始化了一個變量,那麼它會導致像循環語句這樣的其他函數的問題。例如,讓我們說有兩個變量我參數在條件語句和循環語句?該擴展會導致邏輯錯誤。

int i = 0; 
do 
{ 
    int i = 0; 
    i = i+1; 
} 
while(i < 10); 
for(i; i < 20;i++) 
{ 
    code... 
} 

人們只需要一種方法來做事情,爲什麼做兩種不同的方式?

結論:將這種擴展添加到C++語言會在人們開始混淆它時導致各種各樣的騷動和錯誤。所以最好的解決方案是堅持標準。

+2

這個答案比被要求 – sp2danny 2014-12-13 16:23:54

+0

的OP是問理由不同的問題。 – kec 2014-12-13 16:28:25

0

這是不可能的,因爲塊中聲明的變量在該塊中是本地的。 但是,可以在do-while循環之前聲明變量i。 但是,因爲我們不希望的範圍從循環開始延伸,我們可以用一個小竅門:

首先,在你的代碼的開頭加入這一行

#define do(cond) switch (cond) do default: 

。現在

,你可以寫

do (int i = get_data()) { 
    // whatever you want to do with i; 
} while ((i = get_data()) != 0); 

do (int i = 0) { 
    i = get_data(); 
    // whatever you want to do with i; 
} while (i != 0); 

i的範圍僅限於該循環。 #define不會破壞do-while循環的原始用法。 所以下面的語法仍然是有效的:

int j = 0; 
do { 
    // whatever you want to do with j; 
} while (j != 0);