ラベルとgoto

ラベルは、後でプログラムのフローをその行に誘導するために、コードの行に付けられる名前である。

ラベルは、名前と:文字で構成される。

end:   // ← ラベル
D

このラベルは、定義された行に名前を付与する

注釈:実際には、同じ行の文の間にラベルを挿入して、そのラベルが挿入された正確な位置に名前を付けることもできるが、これは一般的な方法ではない。

anExpression(); end: anotherExpression();
D
goto

gotoプログラムの制御を指定されたラベルに誘導する:

void foo(bool condition) {
    writeln("first");

    if (condition) {
        goto end;
    }

    writeln("second");

end:

    writeln("third");
}
D

conditiontrueの場合、プログラムの制御はラベルendに移動し、 "second"と表示する行をスキップする:

最初
3番目

goto CおよびC++プログラミング言語と同じように機能する。コードの意図や流れを理解しにくくなることで悪名高いgotoは、これらの言語でも使用は推奨されていない。代わりに、ifwhileforなどの文を使用すべきだ。

例えば、前のコードは、gotoを使用せずに、より構造化された方法で記述することができる。

// --- Cのコード ---

int foo() {
    // ...

    if (error) {
        goto finally;
    }

    // ...

finally:
    // ... クリーンアップ作業 ...

    return error;
}
C

ただし、Cではgotoの使用が2つの場合のみ許容されるが、Dではいずれも必要ない。

最終化領域

Cでgotoを有効に使用する方法の1つは、関数のクリーンアップ操作(リソースの返却、特定の操作の取り消しなど)を行うファイナライズ領域に移動することだ。

// --- Cのコード ---

    while (condition) {

        while (otherCondition) {

            // 内側のループに影響する
            continue;

            // 内側のループに影響する
            break;

            // 外側のループに対して'continue'のように機能する
            goto continueOuter;

            // 外側のループに対して'break'のように機能する
            goto breakOuter;
        }

    continueOuter:
        ;
    }
breakOuter:
C

Dでは、リソースを管理する他の方法 (ガベージコレクタ、デストラクタ、catchおよびfinallyブロック、scope()文など)があるため、gotoをこのように使用する必要はない。

注釈:このgotoの使用は、C++でも必要ない。

continue breakは外側のループで使用する

Cにおけるgotoの他の有効な使用法は、外側のループに関するものだ。

continuebreakは内側のループにのみ影響するため、外側のループを継続または中断する1つの方法は、goto文を使用することだ。

void foo(bool condition) {
    writeln("first");

    if (!condition) {
        writeln("second");
    }

    writeln("third");
}
D

同じ手法は、外側のswitch文にも使用できる。

Dにはループラベルがあるため、このgotoの使用は必要ない。ループラベルについては後で説明する。

注釈:このgotoの使用法は、C++でも見られる。

コンストラクタのスキップの問題

コンストラクタは、そのオブジェクトが定義された正確な位置で呼び出される。これは主に、オブジェクトを構築するために必要な情報がその時点まで利用できないためだ。また、そのオブジェクトがプログラムで一切使用されない場合、オブジェクトを構築する必要はない。

gotoがオブジェクトが構築されている行をスキップすると、プログラムは未準備のオブジェクトを使用する可能性がある:

if (condition) {
    goto aLabel;    // コンストラクターをスキップする
}

auto s = S(42);     // オブジェクトを正しく構築する

aLabel:

s.bar();            // バグ: 's'が使用可能になっていない可能性がある
D

コンパイラはこのバグを防止する:

エラー: gotoは変数deneme.main.sの宣言をスキップしている
Undefined
ループラベル

ループにはラベルを付けることができ、goto文はそのラベルを参照することができる。

outerLoop:
    while (condition) {

        while (otherCondition) {

            // 内側のループに影響する
            continue;

            // 内側のループに影響する
            break;

            // 外側のループを継続する
            continue outerLoop;

            // 外側のループを中断する
            break outerLoop;
        }
    }
D

switch文にもラベルを付けることができる。内側のbreak文は、外側のswitchを参照して、外側のswitch文を中断することができる。

goto caseセクション内

goto caseの使用は、 switchcaseの章で既に説明した。

概要