C++ 11では、範囲ベースのfor
を使用できます。これは、他の言語のforeach
として機能します。プレーンなC配列でも動作します:
int numbers[] = { 1, 2, 3, 4, 5 };
for (int& n : numbers) {
n *= 2;
}
停止するタイミングはどのようにしてわかりますか? for
が使用されているのと同じスコープで宣言された静的配列でのみ機能しますか?このfor
を動的配列でどのように使用しますか?
タイプが配列であるすべての式で機能します。例えば:
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
をメンバーとして検索することにより動作します。これらの関数は、開始反復子と終了反復子を生成します(それぞれ最後の要素とシーケンスの開始の直後を指します)。
この質問 は、その違いが存在する理由を明確にします。
この質問の最も重要な部分は、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;
最新の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
ループを使用して得られるものよりもはるかに明確です。
静的配列の境界を知っているため、いつ停止するかがわかります。
「動的配列」とはどういう意味かわかりません。静的配列を反復処理しない場合、コンパイラは非公式にスコープ内の名前begin
とend
を検索します反復するオブジェクトのクラスの、または引数依存のルックアップを使用してbegin(range)
およびend(range)
を検索し、それらを反復子として使用します。
詳細については、C++ 11標準(またはその公開草案)の「6.5.4範囲ベースのfor
ステートメント」、pg.145
プレーン配列の範囲ベースの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}
_-配列pl
equals _{5,6,7,8}
_-配列pl
equals _{9,10,11,12}
_-配列プロセスを説明する前に、配列に関するわかりやすい注意事項を示します。
pl
mustは配列をコピーできないため参照する必要がありますn
が問題の数値である場合、_ia[n]
_は同じです*(ia+n)
(前方のn
エントリのアドレスを逆参照しています)、および_ia+n
_は_&ia[n]
_と同じです(そのアドレスを取得しています)配列のエントリ)。ここで何が起こっているのか:
pl
はreferenceとして_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
ループですが、手動で実行せずに現在のループを取得する方法がありません。