コンパイラエラー

C言語のコンパイルエラー C2391の原因と解決方法について解説

Microsoft Visual C++で発生するコンパイラエラー C2391は、クラス内でfriend宣言に完全なクラス定義を記述するときに表示されるエラーです。

friend宣言では、対象となるクラスや関数を前方宣言するなどして指定する必要があります。

コード内のfriend宣言を見直すと、エラーの解消につながります。

エラーC2391の発生背景

C2391エラーは、friend宣言内に型定義が含まれている場合に発生するエラーです。

コンパイラはfriend宣言では完全なクラス定義を許容せず、前方宣言または既に定義済みのクラス名を指定する必要があります。

以下では、エラーメッセージの意味や、どのようなコード記述が原因となるのかを詳しく解説します。

エラーメッセージとその意味

エラーメッセージは通常、次のように表示されます。

「’identifier’ : ‘friend’ は型定義中には使えません」

このメッセージは、friend宣言の中に完全なクラス定義が記述されていることに起因します。

friend宣言では参照するクラスや関数の宣言は可能ですが、宣言と同時にクラス本体を定義することはできません。

エラーメッセージはその問題箇所を特定するための手がかりとなります。

不適切なfriend宣言の事例

friend宣言内でクラスを定義する場合、コンパイラが対象のクラスの完全なレイアウト情報を取得できず、また宣言の意図が不明確になるためエラーが発生します。

たとえば、friendとしてクラスBを定義した場合や、friendとなる関数の宣言の方法に誤りがあると、C2391エラーが発生します。

該当するコード例の解説

以下のコードはエラーC2391を発生させる例です。

#include <stdio.h>
// クラスDの定義
class D {
public:
    void func(int);  // メンバー関数の宣言
};
// クラスAの定義
class A {
    // 以下のfriend宣言はエラーを発生させます
    friend class B { int i; };   // エラー:完全なクラス定義がfriendに含まれている
    // 正常なfriend宣言例
    friend class C;
    friend void D::func(int);
};
int main(void) {
    return 0;
}

上記コードでは、friend class B { int i; }; の部分が問題となります。

friend宣言でBクラスの全体を定義しようとしているため、コンパイラはこれを許容できず、C2391エラーを出力します。

原因の詳細

C2391エラーの根本的な原因は、friend宣言の使い方にあります。

friend宣言は外部のクラスや関数にアクセス権を与えるために用いられますが、正しい記述方法が守られていない場合にエラーが発生します。

friend宣言の仕様

friend宣言は、他のクラスまたは関数がクラスのprivateメンバーやprotectedメンバーへアクセス可能となるようにする宣言です。

friendとして指定される対象は、すでに前方宣言がなされているか、あるいは完全な定義が存在する必要があります。

friend宣言内で新たにクラスの定義を行うことは、言語仕様に反しエラーとなります。

クラス定義中での誤用

クラス定義の途中でfriend宣言として完全なクラス定義を記述すると、C2391エラーが発生します。

これは、コンパイラがfriend対象のクラス情報を正しく認識できず、アクセス権付与の意図が不明確になるためです。

正しくは、friendとして参照するクラスはすでに定義済みであるか、必要ならば前方宣言によって存在を示す必要があります。

完全なクラス定義と前方宣言の違い

完全なクラス定義は、クラスの内部構造(メンバ変数やメンバ関数)まですべてが記述されるのに対し、前方宣言はクラスの名前のみを宣言します。

friend宣言では前方宣言されたクラスを指定することで、

friend class B;

のように記述でき、クラスの詳細な定義を遅延させることができます。

これにより、クラスの完全な定義を分離し、C2391エラーを回避することが可能となります。

解決方法の実践

friend宣言におけるC2391エラーを解決するには、friendに指定する対象を前方宣言済みのクラスや、既に定義済みのクラス・関数の名前に限定する必要があります。

正しい宣言方法に修正することでエラーの解消を図ります。

正しいfriend宣言の記述方法

friend宣言においては、対象となるクラスや関数の宣言があらかじめ行われていることを確認し、以下のようにコードを修正します。

友達対象のクラスは、インラインでの完全定義ではなく、前方宣言を利用して記述することが基本となります。

前方宣言を用いた修正例

まず、friend対象となるクラスBを前方宣言し、その後に必要なクラス定義を行う方法です。

#include <stdio.h>
// クラスBの前方宣言
class B;
// クラスDの定義
class D {
public:
    void func(int);  // メンバー関数の宣言
};
// クラスAの定義
class A {
    // 前方宣言済みのクラスBをfriend宣言
    friend class B;
    // クラスD内の関数をfriend宣言
    friend void D::func(int);
};
// クラスBの定義(前方宣言に対応する定義)
class B {
public:
    int i;  // メンバ変数
};
int main(void) {
    printf("正しいfriend宣言のサンプル\n");
    return 0;
}
正しいfriend宣言のサンプル

上記の例では、クラスBを前方宣言してからfriend宣言に使用しているため、エラーC2391を回避することができます。

修正前後のコード比較

以下の表に、修正前のコードと修正後のコードの違いを示します。

項目 | 修正前 | 修正後

— | — | —

friend宣言 | inlineでクラス定義を記述(例:friend class B { int i; };) | 前方宣言済みのクラス名のみを記述(例:friend class B;)

クラスBの定義 |クラスA内で定義しようとしてエラー発生 |クラスBは前方宣言後に別途定義

このように、前方宣言を利用することで、friend宣言におけるコード記述が簡潔になり、かつコンパイラが正しくクラス情報を解釈できるようになります。

開発環境での留意点

友達宣言の記述方法によりエラーが発生する状況は、使用している開発環境やコンパイラによって異なる場合があります。

ここでは、特にVisual Studioと他のコンパイラとの違いについて触れます。

Visual Studioでの動作確認

Visual Studioでは、friend宣言に関するエラーチェックが厳密に行われています。

たとえば、コンパイラエラーC2391はfriend宣言中に完全なクラス定義が含まれている場合に発生します。

Visual Studioのプロジェクト設定や警告レベルによっては、さらに詳細なエラーメッセージが表示されることがあるので、エラーメッセージをもとに宣言部分を見直すとよいでしょう。

他コンパイラとの違い

GCCやClangなどの他のコンパイラもfriend宣言の記述方法に関しては同様のルールを持っていますが、エラーメッセージが多少異なる場合があります。

各コンパイラはエラーチェックの厳しさや警告の出し方が異なるため、複数の開発環境でコードをビルドする際は、各環境でのエラーメッセージに注意しながら修正することが重要です。

まとめ

本記事では、friend宣言内に完全なクラス定義を記述すると発生するエラーC2391の原因と、そのエラーメッセージの意味について解説しました。

正しい記述方法として前方宣言を利用し、既存のクラスや関数をfriend指定する方法を紹介し、Visual Studioをはじめ他コンパイラにおける動作の違いにも触れました。

これにより、C言語におけるfriend宣言の正しい使い方が理解できる内容となっています。

関連記事

Back to top button
目次へ