16.命令文
この章で明言されていないものは、すべての存在する命令文は標準C++(§6)で仕様規定されている振る舞いをし、それをサポートしています。
16.1 選択命令文
16.1.1 switch命令
finally-cause(ファイナリ節)に遷移する switch 命令文を使うプログラムは不正です。
16.2 イテレーション命令文
標準C++(§6.5)で仕様規定されている三つの順序命令文に加え、
iteration-statement(順序命令文)生産物は for each 命令文を含んだ形に拡張されています。
iteration-statement:
while ( condition ) statement
do statement while ( expression ) ;
for ( for-init-statement conditionopt ; expressionopt ) statement
for#each ( type-specifier-seq declarator in assignment-expression ) statement
16.2.1 for each命令
for each 命令はコレクションの要素を順次照会し、そのコレクションの各要素ごとに
statement(命令文)を実行します。
一緒に、
type-specifier-seq(型指定子シーケンス)と
declarator(宣言子)を命令文の
iteration variable(順次変数)として宣言します。
この順次変数は
statement(命令文)に渡るスコープを持つローカル変数に相当します。
for each 命令の実行中、順次変数は繰り返しが現在行われるコレクションの要素を表現します。
assignment-expression(代入演算)の型は(以下に定義するような)コレクション型であるべきです。
そして、コレクションの要素型から safe_cast を使っての順次変数の型への明示的な変換が可能であるべきです。
もし、
assignment-expression(代入演算)が nullptr 値を持てば、System::NullReferenceException が投げられます。
もし、System::Collections::IEnumerable インターフェイスを実装しているか、System::Collections::Generic::IEnumerable インターフェイスを実装しているか、
以下の基準に全て合致する
コレクション・パターンを実装しているれば、型は
collection-type(コレクション型)であると言います。
| 演算 | 返却型 | 条件/前書き/後置条件 |
| e = c.GetEnumerator() | E | E は enumerator 型 |
| e = c->GetEnumerator() |
| e.MoveNext() | 条件として利用できる値(§14.2.1) | 現在の要素から次の要素への移行に成功したなら true。現在のインスタンスがコレクションの最後に到達したら false 。 |
| e->MoveNext() |
| e.Current | コレクション要素の rvalue, lvalue, gc-lvalue | これはコレクションのelement type (要素型)です。 |
| e->Current |
c は型 T に変換可能なオブジェクトのコレクションであり、e はコレクションを通して順次処理に使える列挙子です。
例え上に上げた条件を満たしていなくても、IEnumerable を実装した型もまたコレクション型です。
(これは明示的インターフェイス・メンバ実装を通して IEnumerable を実装する場合可能です。)
System::Array 型(
§24.1.1)はコレクション型です。
そして、全ての CLI 配列は System::Array 型から派生しているため、任意のCLI配列型表現は for each 命令文に許されます。
一次元CLI配列には、for each 命令文順次処理子は増加順、インデックス 0 に始まり Length -1 で終わる、で、そのCLI配列要素を反復します。
多次元のCLI 配列においては、要素は一番右の次元が最初に増加されて指し示され、それから、次の左の次元で、などなどと左に反芻します。
for each 命令文の形式は、
for each (T d in <collection-expr> ) statement
で、
<collection-expr> は T 型のコレクションであり、GetEnumerator がハンドルを返した場合、次のように記述されているものとして実行されます。
{
<enumeration-type>^ e;
try {
e = <collection-expr>.GetEnumerator();
while ( e->MoveNext() ) {
T d = safe_cast<T>( e->Current );
statement
}
} finally {
delete e;
}
}
e はユーザーがアクセスできない一時的なものであり、<
enumeration-type> は GetEnumerator 関数によって返されるオブジェクトの型です。
もし、GetEnumerator がポインタを返せば、実行はポインタとして宣言された場合を除いて e がハンドルとして宣言された場合と同じです。
もし、GetEnumerator がハンドルかポインタを返さなかった場合には、命令文は次のように書かれているかのように実行されます。
{
<enumeration-type>^ e = <collection-expr>.GetEnumerator();
while ( e.MoveNext() ) {
T d = safe_cast<T>( e.Current );
statement
}
}
[例:次のプログラムはスタックに 0 から 9 の値を押し込んでゆき、それから、for each ループを使ってスタック中の値を上から下の順に表示していきます。
int main() {
Stack<int>^ s = gcnew Stack<int>;
for (int i=0; i < 10; ++i)
s->Push(i);
for each (int i in s)
Console::Write("{0} ", i);
Console::WriteLine();
}
出力された結果は、
9 8 7 6 5 4 3 2 1 0
CLI 配列はコレクション型のインスタンスですので、同様に for each を使うことができ、
int main() {
array<double>^ values = { 1.2, 2.3, 3.4, 4.5 };
for each (double value in values)
Console::WriteLine(value);
}
出力が生成されます。
1.2 2.3 3.4 4.5
]
16.3 ジャンプ命令文
16.3.1 break命令
finally-cause(ファイナリ節)の外に遷移する break 命令文を使うプログラムは不正です。
16.3.2 continue命令
finally-cause(ファイナリ節)の外に遷移する continue 命令文を使うプログラムは不正です。
16.3.3 return命令
finally-cause(ファイナリ節)中に return 命令文を持つプログラムは不正です。
16.3.4 goto命令
finally-cause(ファイナリ節)から抜け出すために、
finally-cause(ファイナリ節)に入るために goto 命令文を使うプログラムは不正です。
16.4 try命令文
nullptr を投げるプログラムは不正です。
標準C++(§15)で規定された文法において、
try-blockと
function-try-block生成はオプションとして
finally-clauseを含むよう次の通り拡張されました。
try-block:
try compound-statement handler-seq
try compound-statement finally-clause
try compound-statement handler-seq finally-clause
function-try-block:
try ctor-initializeropt function-body handler-seq
try ctor-initializeropt function-body finally-clause
try ctor-initializeropt function-body handler-seq finally-clause
finally-clause:
finally compound-statement
finally-clause(ファイナリー節)の命令文は常に
try-block や
function-try-blockの
compound-statement(複合命令文)を制御が外れる時、実行されます。
通常の実行の結果としてか、break, continue, goto ないし、return 命令文の実行結果として、または、
try-block や
function-try-blockの
compound-statement(複合命令文)以外の例外プログラミングの結果として、制御遷移の発生は真となります。
もし、
finally-clause(ファイナリー節)中の命令文の実行の間に例外が投げられたら、その例外は次に閉じている
try-block か
function-try-block に進められます。
もし、他の例外が進行の過程で発生したら、その例外は失われます。
[例:
class MyException {};
void f1();
void f2();
int main() {
try {
f1();
}
catch (const MyException& re) {
...
}
}
void f1() {
try {
f2();
}
finally {
...
}
}
void f2() {
if ( ... ) throw MyException();
}
もし、f2 の呼び出しが通常に帰ったら、finally ブロックは f1 の try ブロックが終了した後に実行されます。
もし、f2 の呼び出しが例外を返した時、finally ブロックは main の catch ブロックが制御を得る前に実行されます。]
[注意:以下のようなプログラムは不正です。
- finally-clause(ファイナリー節)の外に制御を移そうとする break, continue, ないしは goto 命令文
- finally-clause(ファイナリー節)中に return 命令文がある
- finally-clause(ファイナリー節)中に制御を移そうとする goto または switch 命令文
]