サーチ…


前書き

ループ文は、条件が満たされるまで繰り返し文のグループを実行します。 C ++には、for、while、do ... whileの3種類のプリミティブループがあります。

構文

  • while( 条件ステートメント
  • while( )while ステートメント
  • for( init-statement ; 条件 ; expressionステートメント ;
  • for( range-for-range-declarationfor-range-initializerステートメント
  • 休憩。
  • 持続する ;

備考

一般に手書きループよりもalgorithm呼び出しが好ましい。

アルゴリズムが既に何か(あるいは非常に似ている)を望むならば、アルゴリズム呼び出しはより明確になり、しばしばより効率的でエラーの起こりにくいものになります。

非常にシンプルなループが必要な場合(ただし、アルゴリズムを使用している場合は、バインダーとアダプタの混乱が必要です)、ループを作成してください。

範囲ベースのFor

C ++ 11

forループは、数値インデックスを使用せずに、またはイテレータに直接アクセスすることなく、イテレータベースの範囲の要素を反復処理するために使用できます。

vector<float> v = {0.4f, 12.5f, 16.234f};

for(auto val: v)
{
    std::cout << val << " ";
}

std::cout << std::endl;

これは、現在の要素の値を取得するvalで、 vすべての要素を繰り返し処理します。次の文:

for (for-range-declaration : for-range-initializer ) statement

次のものと同等です。

{
    auto&& __range = for-range-initializer;
    auto __begin = begin-expr, __end = end-expr;
    for (; __begin != __end; ++__begin) {
        for-range-declaration = *__begin;
        statement
    }
}
C ++ 17
{
    auto&& __range = for-range-initializer;
    auto __begin = begin-expr;
    auto __end = end-expr; // end is allowed to be a different type than begin in C++17
    for (; __begin != __end; ++__begin) {
        for-range-declaration = *__begin;
        statement
    }
}

この変更は、Ranges TSのC ++での計画サポートのために導入されました20。

この場合、ループは次のようになります。

{
    auto&& __range = v;
    auto __begin = v.begin(), __end = v.end();
    for (; __begin != __end; ++__begin) {
        auto val = *__begin;
        std::cout << val << " ";
    }
}

auto valは値の型を宣言することに注意してください。値の型は範囲に格納された値のコピーになります(私たちはイテレータからコピーを初期化しています)。範囲に格納された値が高価な場合は、 const auto &valを使用することができます。 autoを使う必要もありません。範囲の値型から暗黙的に変換可能である限り、適切な型名を使用できます。

イテレータにアクセスする必要がある場合は、range-based forはあなたを助けることはできません(少なくとも努力する必要はありません)。

それを参照したい場合は、そうすることができます:

vector<float> v = {0.4f, 12.5f, 16.234f};

for(float &val: v)
{
    std::cout << val << " ";
}

constコンテナを持っている場合、 const参照を反復することができます:

const vector<float> v = {0.4f, 12.5f, 16.234f};

for(const float &val: v)
{
    std::cout << val << " ";
}

シーケンス反復子がプロキシオブジェクトを返し、そのオブジェクトをconst方法で操作する必要がある場合、転送参照を使用します。注:あなたのコードの読者を混乱させる可能性が高いでしょう。

vector<bool> v(10);

for(auto&& val: v)
{
    val = true;
}

範囲ベースのfor提供される "range"タイプは、次のいずれかになります。

  • 言語配列:

    float arr[] = {0.4f, 12.5f, 16.234f};
    
    for(auto val: arr)
    {
        std::cout << val << " ";
    }
    

    動的配列の割り当てはカウントされないことに注意してください。

    float *arr = new float[3]{0.4f, 12.5f, 16.234f};
    
    for(auto val: arr) //Compile error.
    {
        std::cout << val << " ";
    }
    
  • メンバ関数begin()end()を持つ型で、型の要素にイテレータを返します。標準ライブラリコンテナは修飾されますが、ユーザ定義型も使用できます。

    struct Rng
    {
        float arr[3];
    
        // pointers are iterators
        const float* begin() const {return &arr[0];}
        const float* end() const   {return &arr[3];}
        float* begin() {return &arr[0];}
        float* end()   {return &arr[3];}
    };
    
    int main()
    {
        Rng rng = {{0.4f, 12.5f, 16.234f}};
    
        for(auto val: rng)
        {
            std::cout << val << " ";
        }
    }
    
  • 非会員有する任意のタイプのbegin(type)及びend(type)に基づいて、引数依存ルックアップを介して見出さできる機能type 。これは、クラスタイプ自体を変更することなく範囲タイプを作成するのに便利です:

    namespace Mine
    {
        struct Rng {float arr[3];};
    
        // pointers are iterators
        const float* begin(const Rng &rng) {return &rng.arr[0];}
        const float* end(const Rng &rng) {return &rng.arr[3];}
        float* begin(Rng &rng) {return &rng.arr[0];}
        float* end(Rng &rng) {return &rng.arr[3];}
    }
    
    int main()
    {
        Mine::Rng rng = {{0.4f, 12.5f, 16.234f}};
    
        for(auto val: rng)
        {
            std::cout << val << " ";
        }
    }
    

ループの場合

forループは、ループconditionが真である間に、 loop bodyステートメントを実行します。ループinitialization statementが正確に1回実行される前。各サイクルの後、 iteration execution部分が実行される。

forループは次のように定義されます。

for (/*initialization statement*/; /*condition*/; /*iteration execution*/)
{
    // body of the loop
}

プレースホルダーステートメントの説明:

  • initialization statement :このステートメントは、 forループの開始時に1回だけ実行さforます。 int i = 0, a = 2, b = 3など、1つの型の複数の変数の宣言を入力できます。これらの変数は、ループの有効範囲内でのみ有効です。同じ名前のループの前に定義された変数は、ループの実行中に隠されます。
  • condition :このステートメントは、各ループ本体の実行の前に評価され、ループが評価されると、ループを中止しfalse
  • iteration execution :ない限り、この文は、先に次の条件評価の、ループ本体の後に実行されますforループは( 体内に中止されたbreakgotoreturnまたはスローされた例外)。 a++, b+=10, c=b+aなど、 iteration execution部分に複数のステートメントを入力できます。

大まかな同等forのように書き換えるループ、 whileループがあります。

/*initialization*/
while (/*condition*/)
{
    // body of the loop; using 'continue' will skip to increment part below
    /*iteration execution*/
}

forループを使用する最も一般的なケースは、特定の回数だけステートメントを実行することです。たとえば、次の点を考慮してください。

for(int i = 0; i < 10; i++) {
    std::cout << i << std::endl;
}

有効なループもあります:

for(int a = 0, b = 10, c = 20; (a+b+c < 100); c--, b++, a+=c) {
    std::cout << a << " " << b << " " << c << std::endl; 
}

ループの前に宣言された変数を隠す例を次に示します。

int i = 99; //i = 99
for(int i = 0; i < 10; i++) { //we declare a new variable i
    //some operations, the value of i ranges from 0 to 9 during loop execution
}
//after the loop is executed, we can access i with value of 99

しかし、すでに宣言されている変数を使用して非表示にしたくない場合は、宣言部を省略します。

int i = 99; //i = 99
for(i = 0; i < 10; i++) { //we are using already declared variable i
    //some operations, the value of i ranges from 0 to 9 during loop execution
}
//after the loop is executed, we can access i with value of 10

ノート:

  • 初期化ステートメントとインクリメントステートメントは、条件ステートメントと無関係な操作を実行することができます。しかし、可読性の理由から、ループに直接関係する操作のみを実行することがベストプラクティスです。
  • 初期化ステートメントで宣言された変数は、 forループのスコープ内でのみ表示され、ループの終了時に解放されます。
  • initialization statement宣言された変数は、ループ中に変更できること、およびconditionチェックされた変数も変更できることを忘れないでください。

0から10までカウントするループの例:

for (int counter = 0; counter <= 10; ++counter)
{
    std::cout << counter << '\n';
}
// counter is not accessible here (had value 11 at the end)

コード断片の説明:

  • int counter = 0は変数counterint counter = 0初期化します(この変数はforループ内でのみ使用できます)。
  • counter <= 10は、 counterが10以下かどうかを調べるブール条件です。 true場合、ループが実行されます。 false場合、ループは終了します。
  • ++counterは、次の条件チェックの前にcounterの値を1ずつインクリメントするインクリメント演算です。

すべてのステートメントを空のままにすると、無限ループを作成できます。

// infinite loop
for (;;)
    std::cout << "Never ending!\n";

上記のwhileループに相当するものは次のとおりです。

// infinite loop
while (true)
    std::cout << "Never ending!\n";

ただし、 breakgoto 、またはreturn文を使用するか、または例外をスローすることによって無限ループを残すことができます。

<algorithm>ヘッダーを使用せずにSTLコレクション(たとえばvector )からすべての要素を反復処理する次の一般的な例は次のとおりです。

std::vector<std::string> names = {"Albert Einstein", "Stephen Hawking", "Michael Ellis"};
for(std::vector<std::string>::iterator it = names.begin(); it != names.end(); ++it) {
    std::cout << *it << std::endl;
}

whileループ

whileループは、指定された条件がfalse評価されるまで、ステートメントを繰り返し実行しfalse 。この制御文は、コードのブロックが何回実行されるかが事前に分かっていない場合に使用されます。

たとえば、0から9までのすべての数値を出力するには、次のコードを使用します。

int i = 0;
while (i < 10)
{
    std::cout << i << " ";
    ++i; // Increment counter
}
std::cout << std::endl; // End of line; "0 1 2 3 4 5 6 7 8 9" is printed to the console
C ++ 17

C ++ 17以来、最初の2つのステートメントを組み合わせることができます

while (int i = 0; i < 10)
//... The rest is the same

無限ループを作成するには、次の構文を使用できます。

while (true)
{
    // Do something forever (however, you can exit the loop by calling 'break'
}

whileループの別の変種、つまりdo...while構文があります。詳細については、 do-whileループの例を参照しください。

条件変数の宣言

forループとwhileループの条件では、オブジェクトを宣言することもできます。このオブジェクトは、ループの終わりまでスコープ内にあるとみなされ、ループの各繰り返しを通して保持されます。

for (int i = 0; i < 5; ++i) {
    do_something(i);
}
// i is no longer in scope.

for (auto& a : some_container) {
    a.do_something();
}
// a is no longer in scope.

while(std::shared_ptr<Object> p = get_object()) {
   p->do_something();
}
// p is no longer in scope.

しかし、 do...whileループで同じことをすることは許されません。代わりに、ループの前に変数を宣言し、ループが終了した後に変数をスコープから外したい場合は、変数とループの両方をローカルスコープ内に囲みます(オプション)。

//This doesn't compile
do {
    s = do_something();
} while (short s > 0);

// Good
short s;
do {
    s = do_something();
} while (s > 0);

これは、 計算書の一部ためであるdo...whileループは(ループの体) の発現部分 (前に評価されてwhile )に達しており、従って、 発現のいずれかの宣言が最初の反復中に表示されなくなりますループ。

Do-whileループ

条件はありません開始時に、各サイクルの終わりでチェックされていることを除いて、DO-whileループ、whileループと非常によく似ています。したがって、ループは少なくとも1回は実行されることが保証されます。

次のコードでは、最初の繰り返しの最後に条件がfalseと評価されるため、 0が出力されます。

int i =0;
do
{
    std::cout << i;
    ++i; // Increment counter
}
while (i < 0);
std::cout << std::endl; // End of line; 0 is printed to the console

注: while(condition);最後にセミコロンを忘れないでくださいwhile(condition);これはdo-while構文で必要とされます。

do-whileループとは対照的に、最初の反復の開始時には条件がfalseと評価されるため、以下は何も印刷しません。

int i =0;
while (i < 0)
{
    std::cout << i;
    ++i; // Increment counter
}    
std::cout << std::endl; // End of line; nothing is printed to the console

注: whileループは、 breakgoto 、またはreturnステートメントを使用して条件が偽になることなく終了することができます。

int i = 0;
do
{
    std::cout << i;
    ++i; // Increment counter
    if (i > 5) 
    {
        break;
    }
}
while (true);
std::cout << std::endl; // End of line; 0 1 2 3 4 5 is printed to the console

簡単なdo-whileループは、独自のスコープを必要とするマクロを書き込むためにも使用されることがあります(その場合、マクロの定義から後続のセミコロンは省略され、ユーザーが提供する必要があります)。

#define BAD_MACRO(x) f1(x); f2(x); f3(x);

// Only the call to f1 is protected by the condition here
if (cond) BAD_MACRO(var);

#define GOOD_MACRO(x) do { f1(x); f2(x); f3(x); } while(0)

// All calls are protected here
if (cond) GOOD_MACRO(var);

ループ制御文:中断し続ける

ループ制御ステートメントは、通常のシーケンスから実行フローを変更するために使用されます。実行がスコープを離れると、そのスコープで作成されたすべての自動オブジェクトが破棄されます。 breakcontinueはループ制御文です。

breakステートメントは、これ以上考慮することなくループを終了します。

for (int i = 0; i < 10; i++)
{
    if (i == 4)
        break; // this will immediately exit our loop
    std::cout << i << '\n';
}

上記のコードが出力されます:

1
2
3

continue文はループをすぐに終了するのではなく、残りのループ本体をスキップしてループの先頭に移動します(条件のチェックを含む)。

for (int i = 0; i < 6; i++)
{
    if (i % 2 == 0) // evaluates to true if i is even
        continue; // this will immediately go back to the start of the loop
    /* the next line will only be reached if the above "continue" statement 
       does not execute  */
    std::cout << i << " is an odd number\n";
}

上記のコードが出力されます:

1 is an odd number
3 is an odd number
5 is an odd number

このような制御フローの変更は、人間が容易に理解することが困難な場合があるため、 breakcontinueは控えめに使用されます。通常、より簡単な実装で読みやすく理解しやすくなります。たとえば、上のbreak持つ最初のforループは次のように書き直されるかもしれません:

for (int i = 0; i < 4; i++)
{
    std::cout << i << '\n';
}

continueの2番目の例は、次のように書き直すことcontinueできます。

for (int i = 0; i < 6; i++)
{
    if (i % 2 != 0) {
        std::cout << i << " is an odd number\n";
    }
}

範囲 - サブ範囲にわたって

範囲ベースのループを使用すると、範囲ベースのforループの対象となるプロキシオブジェクトを生成することによって、特定のコンテナまたは他の範囲のサブパートをループすることができます。

template<class Iterator, class Sentinel=Iterator>
struct range_t {
  Iterator b;
  Sentinel e;
  Iterator begin() const { return b; }
  Sentinel end() const { return e; }
  bool empty() const { return begin()==end(); }
  range_t without_front( std::size_t count=1 ) const {
    if (std::is_same< std::random_access_iterator_tag, typename std::iterator_traits<Iterator>::iterator_category >{} ) {
      count = (std::min)(std::size_t(std::distance(b,e)), count);
    }
    return {std::next(b, count), e};
  }
  range_t without_back( std::size_t count=1 ) const {
    if (std::is_same< std::random_access_iterator_tag, typename std::iterator_traits<Iterator>::iterator_category >{} ) {
      count = (std::min)(std::size_t(std::distance(b,e)), count);
    }
    return {b, std::prev(e, count)};
  }
};

template<class Iterator, class Sentinel>
range_t<Iterator, Sentinel> range( Iterator b, Sentinal e ) {
  return {b,e};
}
template<class Iterable>
auto range( Iterable& r ) {
  using std::begin; using std::end;
  return range(begin(r),end(r));
}

template<class C>
auto except_first( C& c ) {
  auto r = range(c);
  if (r.empty()) return r;
  return r.without_front();
}

今私達はできる:

std::vector<int> v = {1,2,3,4};

for (auto i : except_first(v))
  std::cout << i << '\n';

プリントアウトする

2
3
4

forループのfor(:range_expression)部分で生成される中間オブジェクトは、 forループの開始時に期限切れになることに注意してください。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow