28

StroustrupのC++プログラミング言語:特別版(第3版)では、Stroustrupは、制御ステートメントの条件文での変数の宣言と初期化が許可されているだけでなく、推奨されていると書いています。彼は、変数のスコープを必要なスコープのみに縮小するので、それを奨励すると書いています。だからこのようなもの...

if ((int i = read(socket)) < 0) {
    // handle error
}
else if (i > 0) {
    // handle input
}
else {
    return true;
}

...優れたプログラミングスタイルと実践です。変数は、それが必要とされてスコープ外になるステートメントiのブロックに対してのみ存在します。if

ただし、プログラミング言語のこの機能は、g ++(バージョン4.3.3 Ubuntu固有のコンパイル)ではサポートされていないようです。これは私にとっては驚くべきことです。おそらく、私はそれをオフにするフラグを使用してg ++を呼び出しているだけです(私が呼び出したフラグは-gand-Wallです)。私のバージョンのg++は、これらのフラグを使用してコンパイルすると、次のコンパイルエラーを返します。

socket.cpp:130: error: expected primary-expression before ‘int’
socket.cpp:130: error: expected `)' before ‘int’

さらに調べてみると、これをサポートしていないコンパイラを持っているのは私だけではないようだということがわかりました。そして、この質問には、言語で標準と思われる構文と、それを使用してコンパイルするコンパイラーについて、混乱が生じているようです。

したがって、問題は、どのコンパイラがこの機能をサポートし、コンパイルするためにどのフラグを設定する必要があるかということです。それは特定の基準にあり、他の基準にはないという問題ですか?

また、好奇心から、人々は一般的にこれが良いスタイルであるとStroustrupに同意しますか?それとも、これは言語の作成者が頭の中でアイデアを思いついた状況であり、それは必ずしも言語のコミュニティによってサポートされているわけではありませんか?

4

9 に答える 9

18

ネストされたブロックの制御部分で変数を宣言することは許可されていますが、ifおよびの場合while、変数は条件として解釈される数値またはブール値に初期化する必要があります。より複雑な式に含めることはできません!

あなたが示す特定のケースでは、残念ながら準拠する方法を見つけることができないようです。

個人的には、ローカル変数をコード内の実際の存続期間にできるだけ近づけることをお勧めします。CからC++またはPascalからC++に切り替えたときに衝撃的に聞こえたとしても、すべての変数を一箇所。ある程度の習慣があれば、読みやすくなり、宣言を見つけるために他の場所を探す必要がなくなります。また、それ以前は使用されていないことがわかります。


編集:

そうは言っても、1つのステートメントにあまり多くを混ぜるのは良い習慣ではないと思います。それは共通の意見だと思います。変数の値に影響を与えてから、それを別の式で使用すると、両方の部分を分離することで、コードが読みやすくなり、混乱が少なくなります。

したがって、これを使用するのではなく:

int i;
if((i = read(socket)) < 0) {
    // handle error
}
else if(i > 0) {
    // handle input
}
else {
    return true;
}

私はそれを好むでしょう:

int i = read(socket);
if(i < 0) {
    // handle error
}
else if(i > 0) {
    // handle input
}
else {
    return true;
}
于 2009-10-04T17:46:32.570 に答える
12

おそらくNULLポインターと一緒に使用すると、これは良いスタイルだと思います。

if(CObj* p = GetOptionalValue()) {
   //Do something with p
}

このように、pが宣言されているかどうかにかかわらず、それは有効なポインターです。ダングリングポインタアクセスの危険はありません。

一方、少なくともVC ++では、サポートされている唯一の使用法です(つまり、割り当てがtrueかどうかを確認します)

于 2009-10-04T17:45:12.463 に答える
6

このような状況では、可能な限りconstを使用します。あなたの例の代わりに、私はします:

const int readResult = read(socket);
if(readResult < 0) {
    // handle error
} 
else if(readResult > 0)
{
    // handle input
} 
else {
    return true;
} 

したがって、スコープは含まれていませんが、変数は変更できないため、実際には問題ではありません。

于 2009-10-04T21:45:08.750 に答える
6

彼らはこれをc++17で修正しています:

if (int i = read(socket); i < 0)

ここでif、イニシャライザステートメントを含めることができます。

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0305r0.htmlを参照してください

于 2017-02-17T16:52:23.513 に答える
4

私は同様の問題に遭遇しました:

int問題は、宣言を囲む括弧にあるようです。あなたが課題を表現し、それらなしでテストすることができれば、それはうまくいくはずです、すなわち

if (int i = read(socket)) {

動作するはずですが、それはテストが!= 0であるということを意味します。これはあなたが望むものではありません。

于 2009-10-04T17:43:54.907 に答える
3

ブール式として宣言を使用することはできますが、式の途中に宣言を配置することはできません。あなたがビャルネの言っていることを読み間違えていると思わずにはいられません。

この手法は、主にforループの制御変数に役立ち、望ましいものですが、この場合は、お勧めできず、明確にするのに役立たないと思います。そしてもちろん、それは機能しません!;)

if( <type> <identifier> = <initialiser> ) // valid, but not that useful IMO

if( (<type> <identifier> = <initialiser>) <operator> <operand> )  // not valid

for( <type> <identifier> = <initialiser>; 
     <expression>; 
     <expression> )  // valid and desirable

あなたの例では、条件付きで副作用のある関数を呼び出しましたが、そこで変数を宣言することについて何を考えようとも、IMOは悪い考えです。

于 2009-10-04T17:54:11.510 に答える
3

RedGlyphとFerruccioが言ったことに加えて。条件付きステートメント内で宣言して、その使用を制限するために、次のことを実行できる可能性があります。

if(int x = read(socket)) //x != 0
{
  if(x < 0) //handle error
  {}
  else //do work
  {}
}
else //x == 0  
{
  return true;
}
于 2009-11-11T14:39:16.483 に答える
0

他の人々の良い答えを補完するために、変数の範囲を中かっこでいつでも制限できます。

{    
  const int readResult = read(socket);
  if(readResult < 0) {
    // handle error
  } 
  else if(readResult > 0)
  {
    // handle input
  } 
  else {
    return true;
  } 
}
于 2017-02-17T17:00:38.410 に答える
0

質問に直接関係するわけではありませんが、すべての例でエラー処理が最優先されます。3つのケース(> 0->データ、== 0->接続が閉じられ、<0->エラー)があるため、新しいデータを取得する最も一般的なケースでは2つのテストが必要です。最初に>0をチェックすると、予想されるテスト数がほぼ半分に削減されます。残念ながら、White_Pawnによって提供される「if(intx = read(socket))」アプローチでは、データの場合に2つのテストが必要ですが、C++17の提案を使用して最初に>0をテストできます。

于 2017-04-03T19:26:36.857 に答える