制約のうちの幾つかは finally節に関するものである。
- finallyは省略できない。
- 同一スコープの中では finallyが2回以上記述できない。
同一スコープの中で複数の入れ子関数を定義した場合になにが起こるか、GCCのオンラインドキュメントNested Functionsで調べてみたが、書かれていない。
そこで、幾つかの実験を行って見た。
使用した環境はつぎの通り。
- GCC 4.4.3 on Ubuntu 4.4.3-4ubuntu5.1 (x86)
- GCC 4.6.0 on SunOS 5.11 (x86)
異なるスコープで同じ名称の関数を定義した場合の振る舞い
まず同一スコープで複数回定義した場合の実験の前に、同じ名称の関数を異なるスコープで定義しそれを呼び出した場合になにが実行されるかを試してみる。
次のコードの関数 subが実験コードである。
外側と内側のスコープでは各々 "f(1)"、"f(2)"を表示する関数 fを定義している。
その後、fという名前の関数を呼び出すと呼び出したスコープに一番違い f(2)が呼び出されると思われる。
#include <stdio.h>
void sub() {
void f() { printf("f(1)\n"); }
{
void f() { printf("f(2)\n"); }
f();
}
}
int main() {
sub();
return 0;
}
ビルドして、実行してみる。
$ make NestedFunctionExample1
cc NestedFunctionExample1.c -o NestedFunctionExample1
$ ./NestedFunctionExample1
f(2)
予想通り、f(2)が表示された。
念のため、関数 subを書き換えて、内部の定義が無くとも外部の定義が呼ばれることを確認しておく。
期待通り、外側のスコープで定義した入れ子関数が呼び出された。
void sub() {
void f() { printf("f(1)\n"); }
{
f();
}
}
$ make NestedFunctionExample2
cc NestedFunctionExample2.c -o NestedFunctionExample2
$ ./NestedFunctionExample2
f(1)
期待通り、外側のスコープで定義した入れ子関数が呼び出された。
内側のスコープで 関数 fを定義する前に fという名前で呼び出しを行なおうとしたらコンパイルは通るのか、通ったとしたら何が呼ばれるのかを試してみる。
コンパイルはエラーにならなかった。自動変数の場合もスコープが違えば同じ名前でもちゃんと区別されるので期待通りだ。
内側の定義を行う前と後では呼び出される関数が異なることが判明した。
これは自然な感じがする。
void sub() {
void f() { printf("f(1)\n"); }
{
f();
void f() { printf("f(2)\n"); }
f();
}
}
$ make NestedFunctionExample3
cc NestedFunctionExample3.c -o NestedFunctionExample3
$ ./NestedFunctionExample3
f(1)
f(2)
コンパイルはエラーにならなかった。自動変数の場合もスコープが違えば同じ名前でもちゃんと区別されるので期待通りだ。
内側の定義を行う前と後では呼び出される関数が異なることが判明した。
これは自然な感じがする。
前方参照を用いて、外側の関数定義を呼び出しより後に定義したらコンパイル、実行はどうなるだろうか。
おお、すばらしい。
void sub() {
auto void f();
{
f();
void f() { printf("f(2)\n"); }
f();
}
void f() { printf("f(1)\n"); }
}
$ make NestedFunctionExample4
cc NestedFunctionExample4.c -o NestedFunctionExample4
$ ./NestedFunctionExample4
f(1)
f(2)
おお、すばらしい。
同一スコープで同じ名称の関数を定義した場合の振る舞い
いよいよ、同じ関数名で定義したらどうなるか。
関数fの再定義はコンパイルエラーとなる。
void sub() {
void f() { printf("f(1)\n"); }
f();
void f() { printf("f(2)\n"); }
f();
}
$ make NestedFunctionExample5
cc NestedFunctionExample5.c -o NestedFunctionExample5
NestedFunctionExample5.c: In function ‘sub’:
NestedFunctionExample5.c:11:7: error: redefinition of ‘f’
NestedFunctionExample5.c:9:7: note: previous definition of ‘f’ was here
make: *** [NestedFunctionExample5] Error 1
関数fの再定義はコンパイルエラーとなる。
でも諦めない。なぜなら例外機構では try~catch~finallyを 2個並べてもコンパイルは通っているのだから。
1個目の定義のあと、宣言を入れてみよう。
1個目の定義のあと、宣言を入れてみよう。
void sub() {
void f() { printf("f(1)\n"); }
f();
auto void f();
void f() { printf("f(2)\n"); }
f();
}
vi NestedFunctionExample6.c
$ make NestedFunctionExample6
cc NestedFunctionExample6.c -o NestedFunctionExample6
$ ./NestedFunctionExample6
f(2)
f(2)
コンパイルは通った。
だが、どちらも2個目の定義が呼ばれていて、1個目の定義は無視されている。
これが、例外機構での制約の原因だ。
最初の呼び出しでは1個目の定義のが呼ばれても良いような気がするのだが。
gccのソースを見ていないので憶測だが、同一スコープで同じ名前の関数を定義しようとすると1つ前の実験でわかるように名前の衝突が起きてエラーになるが、
宣言を書けば衝突は回避されるようだ。
しかし、定義の前でも後でも2個目に定義した関数が呼ばれているところを見れば、単純に関数定義を上書きしているのだろう。
宣言は前方参照できるはずなので、定義を呼び出しを逆にしても結果は同じである。
void sub() {
void f() { printf("f(1)\n"); }
f();
auto void f();
f();
void f() { printf("f(2)\n"); }
}
$ make NestedFunctionExample7
cc NestedFunctionExample7.c -o NestedFunctionExample7
$ ./NestedFunctionExample7
f(2)
f(2)
当然だけど、次の例も再定義でエラーになる。
void sub() {
auto void f();
f();
void f() { printf("f(1)\n"); }
f();
void f() { printf("f(2)\n"); }
}
$ make NestedFunctionExample8
cc NestedFunctionExample8.c -o NestedFunctionExample8
NestedFunctionExample8.c: In function ‘sub’:
NestedFunctionExample8.c:13:7: error: redefinition of ‘f’
NestedFunctionExample8.c:11:7: note: previous definition of ‘f’ was here
make: *** [NestedFunctionExample8] Error 1
これまで定義が2個の例を示したが、次のように3個以上でも同じことである。
void sub() {
auto void f();
f();
void f() { printf("f(1)\n"); }
auto void f();
f();
void f() { printf("f(2)\n"); }
auto void f();
f();
void f() { printf("f(3)\n"); }
auto void f();
f();
void f() { printf("f(4)\n"); }
}
$ vi NestedFunctionExample9.c
$ make NestedFunctionExample9
cc NestedFunctionExample9.c -o NestedFunctionExample9
$ ./NestedFunctionExample9
f(4)
f(4)
f(4)
f(4)
以上の結果から、定義と宣言を分けると同一スコープに同じ名称の関数定義を記述することはできるが、最後の定義のみが有効となることが判明した。
これでは現在の例外機構の案では実用にはならない。
gccのバージョンが変わったら挙動が変わる可能性もある。
これでは現在の例外機構の案では実用にはならない。
gccのバージョンが変わったら挙動が変わる可能性もある。
0 件のコメント:
コメントを投稿