2011-10-29 7 views
54

C++ 11では、他の言語のforeachとして機能する範囲ベースのforを使用できます。これはプレーンなC配列でも動作します:プレーン・アレイの作業範囲はどのようになっていますか?

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

いつ停止するのか分かりますか? forと同じスコープで宣言されている静的配列でのみ動作しますか?このforをダイナミックアレイでどのように使用しますか?

+9

CまたはC++自体には "動的な"配列はありません。配列型があり、配列や動的に割り当てられたブロックを指しても指していなくてもよく、主に配列のように動作するポインタがあります。型T [n]の配列の場合、そのサイズは型で符号化され、 'for'でアクセスできます。しかし、配列がポインタに崩壊すると、サイズ情報は失われます。 – JohannesD

+0

あなたの例では、 'numbers'の要素の数は' sizeof(numbers)/ sizeof(int) 'です。 – JohannesD

答えて

37

型が配列であるすべての式に適用されます。たとえば、次の最初の要素を指し、より具体的に説明するための

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

:の右側に渡される式の型が配列型である場合、ptrからptr + sizeに、ループ反復(ptr配列、sizeは配列の要素数です)。

これは、クラスオブジェクトを渡すか、そのように呼ばれるメンバがない場合は非メンバ関数を渡した場合、beginendをメンバとして参照するユーザ定義型とは異なります。これらの関数は、イテレータの開始と終了を返します(最後の要素の直後とシーケンスの先頭をそれぞれ指します)。

This questionは、その違いが存在する理由を解消します。

+8

私はその質問が働いていたと思いますが、それはうまく動作しませんでした。 – sehe

+1

@質問には複数の '?'が含まれていました。 1つは「それは...で動作しますか?」でした。どのように*どうやって*動作するかを説明しました。 –

+7

@ JohannesSchaub:ここでの「どのように」問題は、最初の場所で配列型のオブジェクトのサイズを正確に取得することです(ポインタと配列の混乱のため、ほとんど誰もが配列のサイズ*はプログラマが利用可能です。) – JohannesD

2

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

私は静的配列を反復しないと "動的配列"とは何を意味するのかよく分かりませんが、コンパイラは非公式にオブジェクトのクラスのスコープ内でbeginendの名前を検索します反復処理を行うか、引数に依存する検索を使用してbegin(range)end(range)を検索し、イテレータとして使用します。詳細については、C++ 11標準(またはそのパブリックドラフト)、 "6.5.4範囲ベースfor声明"、pg.145

+4

"動的配列"は 'new []'で作成されたものです。その場合は、サイズの表示がないポインタしかないので、範囲ベースの 'for 'がそのポインタで動作する方法はありません。 –

+0

私の答えにはコンパイル時にsize(4)が分かっている動的配列が含まれていますが、 "dynamic array"の解釈が質問者の意図したものかどうかはわかりません。 –

14

最新のC++ワーキングドラフトによると、中(n3376については

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

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

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

この構文は、私の意見では、あなたがstd::for_eachまたはプレーンforループを使用してになるだろう何よりもはるかに明確です。

+0

これはきちんとしたトリックです! –

19

この質問の最も重要な部分は、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; 
+2

これは私がもともと求めていたものです。 :) –

2

プレーンアレイのための仕事のために範囲ベースどのように?

:ネストされた配列を使用して、次の例を見てください -

を、私はそれを想定してお答えします「?(配列で)何の範囲であった - 私を知らせる」、として読むためにということです

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

テキストバージョン:

iaはそれぞれを含有する、[3]配列を含む、配列の配列( "ネストされたアレイ")であります値。上記の例では、iaが第1の「範囲」([3])でループし、したがって[3]回ループします。各ループはia[3]の最初の値から始まり、最後の値で終わる - [4]の値を含む配列のいずれかを生成します。

  • まずループ:pl{1,2,3,4}等しい - 配列
  • 第二のループ:pl{5,6,7,8}等しい - 配列
  • 第3のループ:pl{9,10,11,12}等しい - 配列

を、我々はプロセスを説明する前に、ここでは配列に関するいくつかの注意を思い出しています:

  • 配列は、彼らの最初の値へのポインタとして解釈されている - 私たちは、配列の配列で
  • 、追加コピーすることはできませんので、任意の反復のない配列が第1の値のアドレスを返す使用
  • pl参照でなければなりません配列オブジェクト自体に数値がある場合は、何度も前に進んで「ポイント」を入力します。nが問題の番号であれば、ia[n]*(ia+n)と同じです(nのエントリを先読みしています) 、ia+n&ia[n]と同じです(私たちはそのエントリのアドレスを取得していますn配列)。

はここで何が起こっているのです:

    各ループで
  • plnだから0から始まる現在のループ回数に等しいと、ia[n]基準とし、plia[0]です第1ラウンドでは、第2ラウンドではia[1]などとなります。反復処理を介して値を取得します。
  • ia+nend(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]; 

を、その後、このプロセスは、参照がないという少し簡単になります反復された値は配列ではなく「通常」の値であるため、が必要です。

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

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

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

いくつかの追加情報

plを作成するときに、我々はautoキーワードを使用しなかった場合は?どのように見えるだろうか?

plは、array of four integersを指します。それはそれは混乱を離れて磨くために追加情報を、どのように動作するかだ

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

そして...:各ループpl上の値ia[n]を与えています。自動的にカウントされるループですが、手動でループを取り出さずに現在のループを取得する方法はありません。

+0

@Andy 10回中9回*タイトルはGoogle /どんな検索でも一致します - タイトルは*どうやって検索しますか?*、いつ*いつ停止するかを知っていますか?そうであっても、根本的な質問は*この答えである程度はカバーされている*他の*答えを探している人のために答える。これらの構文の質問には、タイトルだけが表示されるようにする必要があります。これは、検索者が質問を見つけるために必要なすべての情報であるため、答えを単独で使用することができます。あなたは確かに間違っているわけではありません。 –