2010-07-29

宣言文を飛び越えること

gotoやswitchを使えば、変数の宣言文を飛び越えることが可能になる。

int main()
{
    goto label ;
    int x ; // 宣言文
label : ;
}

このコードは問題のないコードである。しかし、このようなコードを書いてはいけない。なぜなら、「問題のないコード」の定義が、非常に限定的だからである。いま執筆中の本では、この問題について、以下のように記述した。

まず、変数の型は、スカラー型か、trivialなデフォルトコンストラクターとtrivialなデストラクターを持つクラス型でなければならない。また、そのような型にCV修飾子を加えた型と、配列型でもよい。その上で、初期化子が存在していてはならない。

struct POD { } ;
// trivialではないコンストラクターを持つクラス
struct Object { Object() {} } ;

int main()
{
    // 変数の宣言文を飛び越えるgoto文
    goto label ;

    // エラー
    // 変数の型はスカラー型だが、初期化子がある。
    int value = 0 ;

    int scalar ; // OK

    // エラー
    // 変数のクラス型がtrivialではないコンストラクターを持っている
    Object object ;

    POD pod ; // OK

label : ;
}

7 comments:

Anonymous said...

領域確保はするけど初期化コードは走らないよ、っていうことなんでしょうか。
ローカルかつstaticな変数だと初期化できてしまいそうな。。仕様としては不定なんでしょうかne。
int main(){
goto label ;
static int value = 3 ;
label:
}
みたいな。

江添亮 said...

そのコードはまったく問題有りません。
初期化式のあるブロックスコープのstatic変数は、その宣言文が実行されるまでに初期化されます。

Anonymous said...

レスありがとうございます。ためしに動かしてみたのですが、手元のgcc4.1.0だとクラスの初期化の場合はうまく動きませんでした。
int main(){
goto label;
//static int value(3); //OK
static std::string value("3"); //NG. seg fault
label:
std::cout << value;
return 0;
}
これはコンパイラがまずいのか、あるいはプリミティブ型の初期化子とコンストラクタの違いを私が理解してないのか。。

江添亮 said...

static変数の初期化は、宣言文より先に行われる場合もあります。
早期に初期化されない場合、宣言文が最初に実行された際に、初期化されます。

したがって、static変数を使う際には、必ず一度は宣言文を実行しなければなりません。

Anonymous said...

intの場合にたまたま宣言文より先に初期化が行われていたが、保障はされないということですね。理解しました。

江添亮 said...

ただし、ゼロ初期化だけは、あらゆる初期化の前に行わるとされています。

したがって、以下のコードの挙動は保証されています。

int main()
{
goto label ;
static int value ; // ゼロ初期化される
label :
// この場合、valueは0であることが保証されている
if ( value == 0 ) ;
}

萌ゑ said...

うわっ気持ち悪いコードだなぁ

goto + C++のどこでも変数宣言できますよ仕様か

仕様書はどうなってたっけな