「if」ステートメントで変数を初期化する


80

C ++ 17では、次のifようなステートメントで変数を初期化できることを読みました

if (int length = 2; length == 2)
    //execute something

の代わりに

int length = 2;
if (length == 2)
    //do something

短いですが、コードの可読性に影響します(特に、この新機能を知らない人にとって)。これは、大規模なソフトウェア開発では悪いコーディング方法だと思います。

コードを短くする以外に、この機能を使用する利点はありますか?


38
スコープ以外に?
DeiDei

10
数年前に誰かが言ったと思います。「C ++ 11では、次のようなラムダステートメントを作成できます(...)短いですが、コードの読みやすさに影響します(特に知らない人にとって)この新機能)は、大規模なソフトウェア開発にとっては悪いコーディング慣行だと思います。」
R2RT

9
長さはまったく同じで、短くはないと思います。
user78606 7019

5
純粋な意見、したがって答えではありません:if (int length = 2; length == 2)最初にそれを見るのは驚くべきことかもしれませんが、理解できなかったのは複雑ではないので、2回目はもう大きな驚きではなく、それが属する範囲で何かを宣言することは読みやすさに寄与する主な要因の1つ。私はあなたの前提が間違っています;)
maximum_prime_is_4630358 1819

14
コードが書かれている言語(「この新機能がわからない」の意味)を知らない人がコードを読みやすくすることを心配することは、底辺への競争です。

回答:


97

これは、の範囲に制限lengthするifだけでは。だからあなたは私たちが書くことを許可されたときに私たちが最初に得たのと同じ利益を得る

for(int i = 0; i < ... ; ++i) {
   // ...
}

変数リークの代わりに

int i;
for(i = 0; i < ... ; ++i) {
   // ...
}

短期間の変数は、いくつかの理由で優れています。しかし、カップルを挙げれば:

  1. 寿命が短いほど、無関係なコード行を読むときに覚えておく必要のあることが少なくなります。iループまたはifステートメントの外側に存在しない場合、それらの外側でその値を気にする必要はありません。また、その値が、意図したスコープ外にあるプログラムの他の部分と相互作用することを心配する必要もありません(i上記が別のループで再利用された場合に発生する可能性があります)。これにより、コードの追跡と推論が容易になります。

  2. 変数がリソースを保持している場合、そのリソースは可能な限り短い期間保持されます。そして、これは無関係な中括弧なしです。また、リソースがif単独に関連していることも明らかになりました。これをやる気を起こさせる例と考えてください

    if(std::lock_guard _(mtx); guarded_thing.is_ready()) {
    }
    

同僚がこの機能に気付いていない場合は、教えてください。学びたくないプログラマーをなだめることは、機能を避けるための言い訳にはなりません。


12
この最後の文をつかんで、2メートルのポスターに叩きつけます。
クエンティン

3
また、有効期間が短い(スコープが厳密な)変数は、目的が果たされた後のコードで変数を誤って再利用できないため、バグを減らす必要があります。
ガリック

1
または弱いポインタを持つ:if (auto p = ptr.lock(); p && p->foo()) bar(*p);
デュプリケータ

1
3.コンパイラは、より多くの場合にスタックスペースを再利用できます。(たとえば、参照または外部関数へのポインターによってiを渡す場合。)
TLW19年

より良い質問は、「これに対する利点は何{int i = 2; if (i == 2) {...}}
TLW19年

24

コードを短くする以外に、この機能を使用する利点はありますか?

可変スコープを減らします。これは理にかなっており、推論する必要のある識別子の局所性を強化するため、読みやすさが向上します。ステートメント内の長いinitステートメントifは避けるべきであることに同意しますが、短いものの場合は問題ありません。

C ++ 17より前の結果では、すでに初期化と分岐を実行できることに注意してください。

int *get(); // returns nullptr under some condition

if (int *ptr = get())
    doStuff();

これは個人的な意見の対象ですが、明示的な条件をより読みやすく考えることができます。

if (int *ptr = get(); ptr != nullptr)
    doStuff();

その上、人々がそれに慣れていないという事実に言及することによって機能の読みやすさに反対することは危険です。人々はある時点でスマートポインタに慣れていませんでしたが、それでも今日(私は推測します)、彼らがそこにいるのは良いことだと私たちは皆同意しています。


4
if (auto p =get ())演算子boolが定義されているため、使用できます
sudo rm -rfslash19年

19

ifステートメントの新しい形式には多くの用途があります。

現在、初期化子はステートメントの前に宣言されてアンビエントスコープにリークされるか、明示的なスコープが使用されます。新しい形式を使用すると、このようなコードをよりコンパクトに記述でき、スコープコントロールが改善されたことで、以前はエラーが発生しやすい構造が少し堅牢になりました。

イニシャライザを使用してIfステートメントの標準プロポーザルを開く

ここに画像の説明を入力してください

したがって、要約すると、このステートメントは一般的なコードパターンを単純化し、ユーザーがスコープを厳密に保つのに役立ちます。

お役に立てば幸いです。


提案から引用していることを明確にしていただけますか?特に2番目の段落。ブロック引用をお勧めします。
StoryTeller-UnslanderMonica19年

@StoryTellerに感謝します。はい、C ++ 17に組み込まれたopen-stdの提案の2番目の段落を引用しました。
AbhishekSinha19年

10

変数のスコープを最小化するために、作成時に有効な場合にのみリソースを定義するイディオムがあります(たとえば、ファイルストリームオブジェクト)。

if(auto file = std::ifstream("filename"))
{
    // use file here
}
else
{
    // complain about errors here
}

// The identifier `file` does not pollute the wider scope

そのテストのロジックを逆にして、失敗をプライマリ句にし、有効なリソースを句にすることができるようにしたい場合がありますelse。これは以前は不可能でした。しかし今、私たちはできる:

if(auto file = std::ifstream("filename"); !file)
{
    // complain about errors here
}
else
{
    // use file here
}

例として、例外をスローする場合があります。

if(auto file = std::ifstream(filename); !file)
    throw std::runtime_error(std::strerror(errno));
else
{
    // use file here
}

関数がエラーで早期中止され、それ以外の場合は進行するようにコーディングすることを好む人もいます。このイディオムは、一部の人々がより自然に感じるかもしれない継続ロジックよりも物理的に中止ロジックを配置します。


8

これは、論理イベントに特に役立ちます。この例を考えてみましょう。

char op = '-';
if (op != '-' && op != '+' && op != '*' && op != '/') {
    std::cerr << "bad stuff\n";
}

少しラフなようです。OR, AND否定に精通していない限り、一時停止してこのロジックについて考える必要があるかもしれません。これは一般的に貧弱な設計です。if-initializationあなたとあなたは表現力を追加することができます。

char op = '-';
if (bool op_valid = (op == '-') || (op == '+') || (op == '*') || (op == '/'); !op_valid) {
    std::cerr << "bad stuff\n";
} 

名前付き変数は、内部でifも再利用できます。例えば:

if (double distance = std::sqrt(a * a + b * b); distance < 0.5){
    std::cerr << distance << " is too small\n";
}

これは、特に変数のスコープが設定されているため、後でスペースを汚染しないことを考えると、すばらしいことです。


2
主観的だとは思いますが、if-initializerを使用したバージョンよりも「ラフ」バージョンを強くお勧めします。読みやすく、理解しやすいと思います。
ファビオは

@FabioTuratiそれはあなたがそれに非常に精通していて、他のバージョンが新しいからだと思います。しかし、時間の経過とともに、if-initializerが同様のものよりも優れていると思います。
スタックダニー

7

これは既存の機能の拡張であり、私の経験で読みやすくなります。

if (auto* ptr = get_something()) {
}

ここでは、両方とも変数ptrを作成し、それがnullでないことをテストします。の範囲ptrは、有効な場所に限定されます。すべての使用を自分自身に納得させるのははるかに簡単ですptrが有効とです。

しかし、boolそのように変換されない何かについて話している場合はどうなりますか?

if (auto itr = find(bob)) {
}

それはうまくいきません。しかし、この新機能により、次のことが可能になります。

if (auto itr = find(bob); itr != end()) {
}

「この初期化はいつ有効になるか」という句を追加します。

本質的に、これにより、「式を初期化し、有効な場合はコードを実行します。無効な場合は破棄する」という意味のトークンのセットが得られます。

C ++ 98以降、ポインターテストのトリックを行うのは慣用的です。あなたがそれを受け入れたら、この拡張は自然です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.