2011-10-20 28 views
84

ここでは何が起こっていますか? 2003標準で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. 
} 

6.4.3は、選択文の条件で宣言された変数は、条件によって制御されるサブステートメントの最後までスコープを持っているかexpains。しかし、私は宣言の周りにかっこを置くことができないということについては何も言及していませんし、条件ごとに1つの宣言についてしか何も言いません。

この制限は、条件内で1つの宣言だけが必要な場合でも迷惑です。このことを考慮。私はfalseにXが設定された「もし "-body範囲を入力する場合

bool a = false, b = true; 

if(bool x = a || b) 
{ 

} 

は、宣言は(代入演算子は、論理ORよりも低い優先順位を持っているので)括弧が必要ですが、括弧ので、することはできません明らかに、この例は些細なものですが、より現実的な場合は、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

if又はwhile文の条件は、、又は(初期有する)単一可変宣言のいずれかであり得ます。

宣言は式の一部を構成できないため、2番目と3番目の例は有効な式でも有効な宣言でもありません。 3番目の例のようにコードを書くことができれば便利ですが、言語構文を大きく変更する必要があります。

宣言の周りに括弧を置くことができないということはどこには言いませんし、条件ごとに1つの宣言についてしか言いません。

6の構文の仕様。4/1が得られる条件は、次の:

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

なし括弧または他の装飾と、単一の宣言を指定します。あなたはより狭いスコープで変数を囲みたい場合

+1

これには理由や背景はありますか? –

85

あなたはすでにこの問題を暗示していると思います。コンパイラはこのコードで何をすべきですか?

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

"& &" 演算子は、論理短絡回路であり。つまり、最初の部分(1==0)が偽であることが判明した場合、2番目の部分(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

私はそれを考えていませんでした。この例では、短絡を指定すると条件文のスコープが入力されないようになっていますが、処理されない変数を宣言する式の部分は、そのスコープが条件文のスコープに限定されているため問題になりません。 変数を宣言した式の一部が処理されなかったときに条件文の有効範囲が入力される可能性がある場合のみ、コンパイラがエラーを発生させた方が良いでしょうか?私が与えた例ではそうではなかった。 – 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の宣言を周囲のブロックに移します:なぜ特別な状態wrt aとbを持つべきですか? – Giorgio

+0

明らかではありますが、説得力がありません。通常のループ変数でも同じことが言えます。 (しかし、限られた可変スコープの必要性はループではもっと一般的です。) –

17

最後のセクションでは、すでにあなたがわずかに異なることを記述する必要があり、動作します:

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

注意すべき点の1つは、より大きなifブロック内の式が、より大きなifブロック内の式であることです。

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

は必ずしも左から右への評価が保証されているとは限りません。私が1日に戻った微妙なバグの1つは、コンパイラが実際には左から右の代わりに右から左にテストしていたという事実と関係していました。少しテンプレート魔法で

+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; 
} 

しかし、これは他のプログラマを混乱させますし、それはかなり悪いコードですので、お勧めできません。

(既に推奨されているように)読みする方がはるかに簡単です{}ブロックを囲むシンプル:

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

注:

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

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

あなたがis finally possibleをやろうとしたものをC++ 17のよう宣言と実際の条件を区切るには,の代わりに;を使用します。

+0

ニース!私はいつも私が自分の時間より先にいると思った。 – Neutrino