web-dev-qa-db-ja.com

プレーン配列の範囲ベースのforはどのように機能しますか?

C++ 11では、範囲ベースのforを使用できます。これは、他の言語のforeachとして機能します。プレーンなC配列でも動作します:

int numbers[] = { 1, 2, 3, 4, 5 };
for (int& n : numbers) {
    n *= 2;
}

停止するタイミングはどのようにしてわかりますか? forが使用されているのと同じスコープで宣言された静的配列でのみ機能しますか?このforを動的配列でどのように使用しますか?

74
Paul Manta

タイプが配列であるすべての式で機能します。例えば:

int (*arraypointer)[4] = new int[1][4]{{1, 2, 3, 4}};
for(int &n : *arraypointer)
  n *= 2;
delete [] arraypointer;

より詳細な説明については、:の右側に渡される式の型が配列型である場合、ループはptrからptr + sizeptr配列の最初の要素を指し、sizeは配列の要素数です)。

これは、ユーザー定義型とは対照的です。ユーザー定義型は、クラスオブジェクトを渡す場合(またはその方法で呼び出されるメンバーが存在しない場合)、非メンバー関数としてbeginおよびendをメンバーとして検索することにより動作します。これらの関数は、開始反復子と終了反復子を生成します(それぞれ最後の要素とシーケンスの開始の直後を指します)。

この質問 は、その違いが存在する理由を明確にします。

この質問の最も重要な部分は、C++が配列のサイズをどのように知るかということだと思います(少なくとも、この質問を見つけたときに知りたいと思いました)。

C++は、配列の定義の一部であり、変数の型であるため、配列のサイズを認識しています。コンパイラは型を知る必要があります。

C++ 11 std::extentを使用して、配列のサイズを取得できます。

int size1{ std::extent< char[5] >::value };
std::cout << "Array size: " << size1 << std::endl;

もちろん、これはあまり意味がありません。最初の行でサイズを明示的に指定する必要があり、それを2行目で取得するからです。ただし、decltypeを使用することもできます。これにより、さらに興味深い結果が得られます。

char v[] { 'A', 'B', 'C', 'D' };
int size2{ std::extent< decltype(v) >::value };
std::cout << "Array size: " << size2 << std::endl;
32
psur

最新のC++ Working Draft(n3376)によれば、ranged forステートメントは次と同等です。

{
    auto && __range = range-init;
    for (auto __begin = begin-expr,
              __end = end-expr;
            __begin != __end;
            ++__begin) {
        for-range-declaration = *__begin;
        statement
    }
}

したがって、イテレータを使用した通常のforループと同じ方法で停止する方法を知っています。

ポインタとサイズのみで構成される配列(動的配列)で上記の構文を使用する方法を提供するために、次のようなものをお探しかもしれません。

template <typename T>
class Range
{
public:
    Range(T* collection, size_t size) :
        mCollection(collection), mSize(size)
    {
    }

    T* begin() { return &mCollection[0]; }
    T* end () { return &mCollection[mSize]; }

private:
    T* mCollection;
    size_t mSize;
};

その後、このクラステンプレートを使用して範囲を作成し、新しいranged for構文を使用して範囲を反復できます。これを使用して、配列へのポインターと個別の値としてのサイズのみを返すライブラリを使用してインポートされたシーン内のすべてのアニメーションオブジェクトを実行します。

for ( auto pAnimation : Range<aiAnimation*>(pScene->mAnimations, pScene->mNumAnimations) )
{
    // Do something with each pAnimation instance here
}

私の意見では、この構文はstd::for_eachまたはforループを使用して得られるものよりもはるかに明確です。

16
Grant

静的配列の境界を知っているため、いつ停止するかがわかります。

「動的配列」とはどういう意味かわかりません。静的配列を反復処理しない場合、コンパイラは非公式にスコープ内の名前beginendを検索します反復するオブジェクトのクラスの、または引数依存のルックアップを使用してbegin(range)およびend(range)を検索し、それらを反復子として使用します。

詳細については、C++ 11標準(またはその公開草案)の「6.5.4範囲ベースのforステートメント」、pg.145

3
chill

プレーン配列の範囲ベースのforはどのように機能しますか?

ranged-forが(配列で)何をするのか教えてください?

私はそれを仮定して答えます-ネストされた配列を使用して次の例を取り上げます。

_int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};

for (auto &pl : ia)
_

テキスト版:

iaは、それぞれ__[3]_値を含む_[4]_配列を含む配列の配列(「ネストされた配列」)です。上記の例は、プライマリ[範囲](_[3]_)でiaをループするため、_[3]_回ループします。各ループは、最初から始まり最後で終わるiaの_[3]_プライマリ値の1つを生成します-_[4]_値を含む配列。

  • 最初のループ:pl equals _{1,2,3,4}_-配列
  • 2番目のループ:pl equals _{5,6,7,8}_-配列
  • 3番目のループ:pl equals _{9,10,11,12}_-配列

プロセスを説明する前に、配列に関するわかりやすい注意事項を示します。

  • 配列は最初の値へのポインターとして解釈されます-繰り返しなしで配列を使用すると、最初の値のアドレスが返されます
  • plmustは配列をコピーできないため参照する必要があります
  • 配列では、配列オブジェクト自体に数値を追加すると、それが何度も前方に進み、同等のエントリを「ポイント」します-nが問題の数値である場合、_ia[n]_は同じです*(ia+n)(前方のnエントリのアドレスを逆参照しています)、および_ia+n_は_&ia[n]_と同じです(そのアドレスを取得しています)配列のエントリ)。

ここで何が起こっているのか:

  • 各ループで、plreferenceとして_ia[n]_に設定され、nは0から始まる現在のループカウントに等しくなります。 plは、最初のラウンドでは_ia[0]_であり、2番目のラウンドでは_ia[1]_などです。反復により値を取得します。
  • ループは、_ia+n_がend(ia)より小さい限り続きます。

...それで終わりです。

それは本当にただこれを書く簡単な方法

_int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
for (int n = 0; n != 3; ++n)
  auto &pl = ia[n];
_

配列がネストされていない場合、このプロセスは、参照がnot必要であるという点で少し単純になります。値は配列ではなく、「通常の」値です。

_ int ib[3] = {1,2,3};

 // short
 for (auto pl : ib)
   cout << pl;

 // long
 for (int n = 0; n != 3; ++n)
   cout << ib[n];
_

追加情報

autoの作成時にplキーワードを使用したくない場合はどうなりますか?それはどのように見えるでしょうか?

次の例では、plは_array of four integers_を指します。各ループでplには値_ia[n]_が与えられます:

_int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
for (int (&pl)[4] : ia)
_

そして...混乱を一掃するための追加情報とともに、それがどのように機能するかです。これは、自動的にカウントされる「短縮形」forループですが、手動で実行せずに現在のループを取得する方法がありません。

2
Super Cat