[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.4 入れ子関数

ネストした関数 とは、なにか別の関数のなかで定義された関数である。 (GNU C++ ではネストした関数はサポートしていない。) ネストした関数の名前は、定義されたブロック内で有効である。 以下の例では、square という名前のネストした関数を定義し、 二回呼び出している。

 
foo (double a, double b)
{
  double square (double z) { return z * z; }

  return square (a) + square (b);
}

ネストした関数からは、それを含む関数の変数のうち、ネストした関数 が定義されている点で見えるものは全部参照可能である。 これは、レキシカル・スコーピング と呼ばれている。 例えば、以下の例では、ネストした関数が offset という名前の変数を 親関数から継承している。

 
bar (int *array, int offset, int size)
{
  int access (int *array, int index)
    { return array[index + offset]; }
  int i;
  …
  for (i = 0; i < size; i++)
    … access (array, i) …
}

ネストした関数は、関数内の、変数の定義が出来る場所で定義出来る。 つまり、任意のブロック中の最初の文の前になる。

ネストした関数をその名前のスコープの外側から呼び出すのは、 そのアドレスを格納したり、別の関数にアドレスを渡したりすることで 可能である。

 
hack (int *array, int size)
{
  void store (int index, int value)
    { array[index] = value; }

  intermediate (store, size);
}

ここで、関数 intermediate は、引数として store の アドレスを受け取る。 intermediatestore を呼び出すなら、 その時に store に与えられる引数が、array に 格納するのに使われる。 だが、この方法は、そのネストした関数を包含する関数(この例では、hack) が終了しない間だけ、機能する。

ネストした関数を、そのアドレスを使って、その包含関数が終了した後で 呼び出そうとすると、大変なことになる。 包含しているスコープレベルを抜けた後に呼び出しを行なうとした場合、 もはやスコープにない変数を何か参照していると、運が良ければうまく いくかもしれないが、危険を犯すことになる。 だが、ネストした関数がスコープから出てしまったものを何も 参照していない場合は、安全である。

GNU CC はネスト関数のアドレスを取るのに、トランポリン と 呼ばれる方法を使って実装している。これについて説明している 論文が ‘http://master.debian.org/~karlheg/Usenix88-lexic.pdf’ から利用できる。

ネスト関数は、包含関数内でラベルが明示的に宣言されていれば、 包含関数から継承したそのラベルにジャンプすることができる (see section 局所的に宣言されたラベル)。 そういうジャンプは直ちに包含関数に戻り、goto を実行したネスト関数 と中間の関数を終了する。 以下に例を示す。

 
bar (int *array, int offset, int size)
{
  __label__ failure;
  int access (int *array, int index)
    {
      if (index > size)
        goto failure;
      return array[index + offset];
    }
  int i;
  …
  for (i = 0; i < size; i++)
    … access (array, i) …
  …
  return 0;

 /* Control comes here from access
    if it detects an error.  */
 failure:
  return -1;
}

ネスト関数は常に内部リンケージとなる。 extern 付きで宣言するのは誤りである。 ネスト関数を、その定義の前に宣言する必要があるときは、 auto を使うこと(これは、この場合以外の関数宣言では意味がない。)

 
bar (int *array, int offset, int size)
{
  __label__ failure;
  auto int access (int *, int);
  …
  int access (int *array, int index)
    {
      if (index > size)
        goto failure;
      return array[index + offset];
    }
  …
}

This document was generated using texi2html 1.78.