2011-10-20 37 views
84

這是怎麼回事?C++,'if'表達式中的變量聲明

if(int a = Func1()) 
{ 
    // Works. 
} 

if((int a = Func1())) 
{ 
    // Fails to compile. 
} 

if((int a = Func1()) 
    && (int b = Func2())) 
) 
{ 
    // Do stuff with a and b. 
    // This is what I'd really like to be able to do. 
} 

在2003年的標準expains在選擇語句條件中聲明的變量怎麼也延伸到由條件控制的substatements結束範圍6.4.3節。但是我沒有看到它說什麼不能在括號中加括號,也沒有說每個條件只有一個聲明。

即使在只需要一個聲明的情況下,這個限制也很煩人。考慮這一點。

bool a = false, b = true; 

if(bool x = a || b) 
{ 

} 

如果我想進入與X設置了「如果「體區範圍爲假,則需要申報括號(因爲賦值運算符比邏輯或優先級低),但由於括號不能使用它需要在體外​​聲明x,將聲明泄漏到比想要的更大的範圍,顯然這個例子是微不足道的,但更實際的情況是a和b是返回需要測試的值的函數

那麼我想做的是不符合標準,還是我的編譯器只是在破解我的球(VS2008)?

+6

「如果我想用」< - 你的例子有'if'來輸入循環。 'if'不是一個循環,它是一個條件。 – crashmstr

+2

@crashmstr:true,但'while'的條件與'if'的條件相同。 –

+2

這不能用逗號操作符來完成嗎?我的意思是:'if(int a = foo(),int b = bar(),a && b)'?如果逗號運算符沒有超載,則該標準說明表達式從左到右進行計算,結果值是最後一個表達式。它適用於for循環初始化,爲什麼不在這裏? – Archie

回答

83

ifwhile語句中的條件可以是一個表達,或一個單可變聲明(用初始化)。

您的第二個和第三個示例既不是有效的表達式,也不是有效的聲明,因爲聲明不能構成表達式的一部分。雖然能夠像第三個示例一樣編寫代碼會很有用,但它需要對語言語法進行重大更改。

我沒有看到它說什麼不能在括號內放置括號,也沒有說每個條件只有一個聲明。

6中的語法規範。4/1給出了條件如下:

condition: 
    expression 
    type-specifier-seq declarator = assignment-expression 

指定單個聲明,沒有括號或其他裝飾品。

+1

這是否有任何理由或背景? –

85

我想你已經暗示了這個問題。編譯器應該如何處理這些代碼?

if (!((1 == 0) && (bool a = false))) { 
    // what is "a" initialized to? 

的 「& &」 操作符是短路邏輯AND。這意味着如果第一部分(1==0)證明是錯誤的,那麼應該不對第二部分(bool a = false)進行評估,因爲已經知道最終答案是錯誤的。如果(bool a = false)未被評估,那麼稍後如何處理代碼使用a?我們是否只是不初始化變量並使其不確定?我們將它初始化爲默認值?如果數據類型是一個類,那麼會發生不希望的副作用?如果不是bool你使用了一個類,並且它沒有默認構造函數,那麼用戶必須提供參數,那麼我們該怎麼做呢?

再舉一例:

class Test { 
public: 
    // note that no default constructor is provided and user MUST 
    // provide some value for parameter "p" 
    Test(int p); 
} 

if (!((1 == 0) && (Test a = Test(5)))) { 
    // now what do we do?! what is "a" set to? 

好像你已經找到了限制似乎完全合理的 - 它可以防止這類模糊的情況的發生。

+0

好點。如果OP或其他人不熟悉它,你可能要明確提及短路。 –

+6

我沒有想到這一點。儘管在本例中提供了短路防止輸入條件語句範圍,但在這種情況下,聲明變量不被處理的表達式部分不是問題,因爲它的範圍僅限於條件語句的範圍。 在這種情況下,豈不是更好,如果編譯器提出只有在這些情況下,有條件語句範圍的possibilty錯誤時沒有處理那個聲明的變量表達式的一部分被輸入?在我給出的例子中,情況並非如此。 – Neutrino

+0

@Neutrino初看起來,你的想法聽起來有點像SAT-問題,至少在一般情況下這並不容易解決。 –

19

如果你想封裝在一個較窄的範圍內的變量,你總是可以使用額外的{ }

//just use { and } 
{ 
    bool a = false, b = true; 

    if(bool x = a || b) 
    { 
     //... 
    } 
}//a and b are out of scope 
+5

+1。另外,我會將x的聲明移動到周圍的塊:爲什麼它應該具有a和b的特殊狀態? – Giorgio

+0

顯而易見,但並不令人信服:對於普通的循環變量也是如此。 (當然,循環中需要有限的變量範圍。) –

17

最後一節已經工作,你就必須把它寫稍有不同:

if (int a = Func1()) 
{ 
    if (int b = Func2()) 
    { 
     // do stuff with a and b 
    } 
} 
1

需要注意的一件事,也是在較大的if塊內的表達式

if (!((1 == 0) && (bool a = false))) 

不一定保證以從左到右的方式進行評估。我當天回來的一個非常微妙的錯誤與編譯器實際上是從右到左而不是從左到右的測試有關。

+4

然而,現在,C99要求&&和||從左到右進行評估。 – b0fh

+1

由於短路邏輯,我認爲從來沒有可能從右到左評估參數。它總是用於像單個表達式中的指針和指針對象的測試,比如'if(p && p-> str && * p-> str)...'。從右到左會是致命的,而不是微妙的。與其他運營商 - 甚至分配! - 和函數調用參數你是對的,但不適用於短路操作符。 –

0

一個小模板魔你可以圍繞不能夠的問題,那種幾分去聲明多個變量:

#include <stdio.h> 

template <class LHS, class RHS> 
struct And_t { 
    LHS lhs; 
    RHS rhs; 

    operator bool() { 
    bool b_lhs(lhs); 
    bool b_rhs(rhs); 
    return b_lhs && b_rhs; 
    } 
}; 
template <class LHS, class RHS> 
And_t<LHS, RHS> And(const LHS& lhs, const RHS& rhs) { return {lhs, rhs}; } 

template <class LHS, class RHS> 
struct Or_t { 
LHS lhs; 
RHS rhs; 

    operator bool() { 
    bool b_lhs(lhs); 
    bool b_rhs(rhs); 
    return b_lhs || b_rhs; 
    } 
}; 
template <class LHS, class RHS> 
Or_t<LHS, RHS> Or(const LHS& lhs, const RHS& rhs) { return {lhs, rhs}; } 

int main() { 
    if (auto i = And(1, Or(0, 3))) { 
    printf("%d %d %d\n", i.lhs, i.rhs.lhs, i.rhs.rhs); 
    } 
    return 0; 
} 

(注意,這失去了短路的評價。)

+0

我認爲它*失去*(或放鬆?) –

+2

我認爲OP的意圖是代碼簡潔,清晰和可維護性。你提出的「解決方案」是相反的。 –

2

這裏是一個醜陋的解決方法使用一個循環(如果兩個變量均爲整數):

#include <iostream> 

int func1() 
{ 
    return 4; 
} 

int func2() 
{ 
    return 23; 
} 

int main() 
{ 
    for (int a = func1(), b = func2(), i = 0; 
     i == 0 && a && b; i++) 
    { 
     std::cout << "a = " << a << std::endl; 
     std::cout << "b = " << b << std::endl; 
    } 

    return 0; 
} 

但是,這會迷惑其他程序員,它是相當糟糕的代碼,因此不建議使用。

一個簡單的封閉{}塊(如已推薦)是更易於閱讀:

{ 
    int a = func1(); 
    int b = func2(); 

    if (a && b) 
    { 
     std::cout << "a = " << a << std::endl; 
     std::cout << "b = " << b << std::endl; 
    } 
} 
11

由於C++ 17你試圖做is finally possible

if (int a = Func1(), b = Func2(); a && b) 
{ 
    // Do stuff with a and b. 
} 

注意使用;而不是,來區分聲明和實際情況。

+0

不錯!我一直懷疑我超前了我的時間。 – Neutrino