コンテンツにスキップ

前方宣言

出典: フリー百科事典『ウィキペディア(Wikipedia)』

プログラミングにおいて、前方宣言(ぜんぽうせんげん、: forward declaration)とはプログラマが完全な定義を与えていないプログラム要素を表すシンボルの事前宣言のことである。変数関数などに対するシンボルが含まれる。

概要

[編集]

C言語の例を示す。

int elements[]; /* int型の配列変数の前方宣言 */
void foo(int); /* int型の引数を受け取る関数の前方宣言 */
struct user_type; /* 構造体の前方宣言 */

関数の前方宣言はまた関数プロトタイプでもある。コンパイラはソースコード中に出現したこれらの宣言を処理した後、プログラマに以降の部分でelements, foo, user_typeの実体の使用を許可する。ただし、宣言はあくまでプログラムにおける各シンボルの意味(役割)を示すだけである。また、要素数が明らかでない静的配列や、メンバーが分からない構造体は不完全な型 (incomplete type) であり、sizeof演算子を適用したり、メンバーにアクセスしたりすることはできない。最終的にプログラマは(例えば以下のような)宣言した実体のための定義をどこかで提供しなければならない。

int elements[10];
void foo(int x) { printf("%d\n", x); }
struct user_type { int a; double t; };

大抵のケースでは、実体の定義の順序を工夫することで、前方宣言をせずともプログラムを記述することはできるが、循環参照を解決したり、自己参照構造体を定義したり、ソースコードの解析回数を減らして効率よくプログラムをコンパイルしたりするために前方宣言が用いられる。特に前方宣言をインターフェイスとしてヘッダーファイルに分離し、実体をソースファイルに記述することで、個々のプログラム部品の実装を隠蔽して独立性を高めることができるようになる。

Pascalや他のヴィルトの言語では、前方宣言は使う前にすべての実体を宣言しなければならないという一般的規則である。C言語でも同じ一般的規則が適用されるが、未宣言の関数のための例外がある。その結果、C言語では(賢明ではないが)相互再帰の関数ペアを実行することが可能である:

int first(int x)
{
    if (x == 0) return 1;
    return second(x-1);
}

int second(int x)
{
    if (x == 0) return 0;
    return first(x-1);
}

Pascalで同様の実装をする場合、firstsecondを呼び出す前にsecondの前方宣言が必要である。前方宣言がなければ、コンパイラは識別子 secondが宣言されないで使われたというエラーメッセージを表示するであろう。

C++では構造体 (struct) に加えてクラス (class) の前方宣言をサポートするほか、C++11規格では列挙型 (enum) の前方宣言をサポートするようになった。

前方参照

[編集]

前方参照 (: forward reference) という用語は時々前方宣言類義語として使われている。[1] しかしながらこの用語はその定義よりもむしろ、宣言する前に実体を実際に「利用」することを言及するのに使われている。この定義は上記のコードにおけるfirstsecondへの参照が前方参照ということである。[2][3] したがって私達は、Pascalでは前方宣言が必須であるので、前方「参照」が禁止されていると言うであろう。

C++での(正しい)前方参照の例:

class SomeClass {
public:
    void setValue(int v) { myValue = v; }
    int getValue() const { return myValue; }
private:
    int myValue;
};

例えば、myValueの宣言前にmyValueへの二つの参照があるとする。C++では一般的に前方参照を禁止しているが、上記コードはクラスメンバの例外として許されている。コンパイラは、メンバ変数myValueの型を知らないとメンバ関数getValue()をコンパイルできないので、myValueの宣言を処理するまでgetValue()の定義を覚えておく。

前方参照を可能にすると、コンパイラの複雑さやメモリの必要量が激しく増し、一般的にはコンパイラのワンパスでの実行を難しくする。