C ++ 11では、他の言語のfor
ように機能する範囲ベースを使用できますforeach
。プレーンなC配列でも機能します。
int numbers[] = { 1, 2, 3, 4, 5 };
for (int& n : numbers) {
n *= 2;
}
いつ停止するかをどうやって知るのですか?for
が使用されているのと同じスコープで宣言されている静的配列でのみ機能しますか?これfor
を動的配列でどのように使用しますか?
C ++ 11では、他の言語のfor
ように機能する範囲ベースを使用できますforeach
。プレーンなC配列でも機能します。
int numbers[] = { 1, 2, 3, 4, 5 };
for (int& n : numbers) {
n *= 2;
}
いつ停止するかをどうやって知るのですか?for
が使用されているのと同じスコープで宣言されている静的配列でのみ機能しますか?これfor
を動的配列でどのように使用しますか?
numbers
はsizeof(numbers)/sizeof(int)
、の要素の数は、たとえばです。
回答:
これは、型が配列であるすべての式に対して機能します。例えば:
int (*arraypointer)[4] = new int[1][4]{{1, 2, 3, 4}};
for(int &n : *arraypointer)
n *= 2;
delete [] arraypointer;
より詳細な説明のために、の右側に渡される:
式の型が配列型である場合、ループはptr
からptr + size
(ptr
配列の最初の要素を指し、配列size
の要素数)から繰り返されます。
これは、調べても作業ユーザー定義型とは対照的であるbegin
とend
非メンバ関数(そのように呼ばれる何のメンバーが存在しない場合)は、クラスオブジェクトまたはを渡す場合メンバーとして。これらの関数は、開始イテレータと終了イテレータを生成します(それぞれ最後の要素とシーケンスの開始の直後を指します)。
この質問は、なぜその違いが存在するのかを明らかにします。
begin
. It just happens that
`std::end
この質問の最も重要な部分は、C ++が配列のサイズをどのように知っているかということだと思います(少なくとも、この質問を見つけたときに知りたかったのです)。
C ++は配列のサイズを認識しています。これは、配列の定義の一部であり、変数の型であるためです。コンパイラは型を知っている必要があります。
C ++ 11std::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;
最新のC ++ワーキングドラフト(n3376)によると、rangedforステートメントは次のようになります。
{
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;
};
次に、このクラステンプレートを使用して範囲を作成し、その範囲を新しいrangedfor構文を使用して繰り返すことができます。これを使用して、配列へのポインターとサイズを個別の値として返すだけのライブラリを使用してインポートされたシーン内のすべてのアニメーションオブジェクトを実行しています。
for ( auto pAnimation : Range<aiAnimation*>(pScene->mAnimations, pScene->mNumAnimations) )
{
// Do something with each pAnimation instance here
}
私の意見では、この構文は、使用する構文std::for_each
や単純なfor
ループよりもはるかに明確です。
静的配列の境界を知っているので、いつ停止するかを知っています。
「動的配列」とはどういう意味かわかりません。いずれにせよ、静的配列を反復処理しない場合、コンパイラーは非公式に、反復処理するオブジェクトのクラスの名前begin
とend
スコープを検索します。以下のためのアップbegin(range)
とend(range)
引数依存の検索やイテレータなどの用途にそれらを使用します。
詳細については、C ++ 11標準(またはその公開ドラフト)の「6.5.4範囲ベースのfor
ステートメント」、145ページを参照してください。
new[]
ます。その場合、サイズが示されていないポインタしかないため、範囲ベースでポインタを操作する方法はありませんfor
。
プレーン配列の範囲ベースの作業はどのように行われますか?
それは、と読むことです「を教えてくださいどのような範囲であった-用(配列で)いますか?」
私はそれを仮定して答えます-ネストされた配列を使用して次の例を見てください:
int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
for (auto &pl : ia)
テキストバージョン:
ia
は配列の配列(「ネストされた配列」)であり、[3]
配列を含み、それぞれに[4]
値が含まれています。上記の例は、そのia
プライマリ '範囲'([3]
)によってループスルーするため、[3]
時間をループします。各ループは、最初から最後までia
の[3]
プライマリ値の1つを生成します-[4]
値を含む配列。
pl
等しい{1,2,3,4}
-配列pl
等しい{5,6,7,8}
-配列pl
等しい{9,10,11,12}
-配列プロセスを説明する前に、配列に関するいくつかのわかりやすい注意事項を次に示します。
pl
配列をコピーできないため、参照である必要がありますn
が問題の数値である場合は、(エントリであるアドレスを逆参照しています)ia[n]
と同じ*(ia+n)
です。n
forward)でありia+n
、&ia[n]
(配列内のそのエントリのアドレスを取得しています)と同じです。何が起こっているのか:
pl
のように設定されている基準にia[n]
して、n
0だからから起動電流ループ回数に等しい、pl
であるia[0]
ことがだ第二に、最初のラウンドで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];
配列がネストされていない場合、反復値は配列ではなく「通常の」値であるため、このプロセスは参照が不要であるという点で少し単純になります。
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
、自動的にカウントされる単なる「短縮」ループですが、手動で実行せずに現在のループを取得する方法がありません。
スタック上の配列とヒープ上の配列の違いを示すサンプルコード
/**
* Question: Can we use range based for built-in arrays
* Answer: Maybe
* 1) Yes, when array is on the Stack
* 2) No, when array is the Heap
* 3) Yes, When the array is on the Stack,
* but the array elements are on the HEAP
*/
void testStackHeapArrays() {
int Size = 5;
Square StackSquares[Size]; // 5 Square's on Stack
int StackInts[Size]; // 5 int's on Stack
// auto is Square, passed as constant reference
for (const auto &Sq : StackSquares)
cout << "StackSquare has length " << Sq.getLength() << endl;
// auto is int, passed as constant reference
// the int values are whatever is in memory!!!
for (const auto &I : StackInts)
cout << "StackInts value is " << I << endl;
// Better version would be: auto HeapSquares = new Square[Size];
Square *HeapSquares = new Square[Size]; // 5 Square's on Heap
int *HeapInts = new int[Size]; // 5 int's on Heap
// does not compile,
// *HeapSquares is a pointer to the start of a memory location,
// compiler cannot know how many Square's it has
// for (auto &Sq : HeapSquares)
// cout << "HeapSquare has length " << Sq.getLength() << endl;
// does not compile, same reason as above
// for (const auto &I : HeapInts)
// cout << "HeapInts value is " << I << endl;
// Create 3 Square objects on the Heap
// Create an array of size-3 on the Stack with Square pointers
// size of array is known to compiler
Square *HeapSquares2[]{new Square(23), new Square(57), new Square(99)};
// auto is Square*, passed as constant reference
for (const auto &Sq : HeapSquares2)
cout << "HeapSquare2 has length " << Sq->getLength() << endl;
// Create 3 int objects on the Heap
// Create an array of size-3 on the Stack with int pointers
// size of array is known to compiler
int *HeapInts2[]{new int(23), new int(57), new int(99)};
// auto is int*, passed as constant reference
for (const auto &I : HeapInts2)
cout << "HeapInts2 has value " << *I << endl;
delete[] HeapSquares;
delete[] HeapInts;
for (const auto &Sq : HeapSquares2) delete Sq;
for (const auto &I : HeapInts2) delete I;
// cannot delete HeapSquares2 or HeapInts2 since those arrays are on Stack
}
for
。しかし、配列がポインタに減衰する瞬間、サイズ情報は失われます。