Microsoft Visual StudioのC++で発生するコンパイルエラーC2990について解説
Visual StudioのC++コンパイラで発生するエラーC2990は、同じ名前の型が既に宣言されている場合に、非ジェネリッククラスや非テンプレートクラスを再定義しようとすると発生します。
たとえば、テンプレートクラスを先に定義してから通常のクラスを同名で宣言するとエラーになります。
ヘッダーファイルなどを見直し、重複宣言を解消することで対処できます。
エラーC2990の基本情報
このセクションでは、エラーC2990の基本的な意味と定義について解説します。
エラーC2990は、Microsoft Visual StudioのC++コンパイラで発生するコンパイルエラーの一つで、クラスの定義に関する競合が原因で発生します。
具体的には、テンプレートクラスやジェネリッククラスの定義と、非テンプレート(または非ジェネリック)クラスの定義が同一の名前で重複している場合に起こります。
エラーの意味と定義
エラーC2990は、同じ名前のクラスが複数の形態で定義されている場合に発生します。
たとえば、以下のような状況が該当します。
- テンプレートクラスとして定義しているにもかかわらず、同じ名前で非テンプレートクラスが定義された場合
- ジェネリッククラス(/clrオプション使用時)として宣言しているにも関わらず、非ジェネリッククラスが同じ名前で定義された場合
このエラーが発生する理由は、コンパイラが同一の名前に対し、異なる型情報を与える重複定義を許容しないためです。
すなわち、コンパイラはひとつの名前に対して統一した型定義が存在することを要求しています。
数学的には、同一属性
エラーメッセージの解読
実際にエラーが発生した場合、コンパイル時に以下のようなエラーメッセージが表示されることがあります。
'class' : 非クラス型は、クラス型として既に宣言されています
このメッセージは、名前C
やGC
などが、既にクラス型として宣言されているにもかかわらず、別の形態(テンプレートやジェネリック)で宣言が重複していることを示しています。
エラーメッセージは、どの部分に重複が発生しているかを特定するための手がかりとなるため、ソースコード中の定義を確認する際に参考にすることが可能です。
エラー発生の背景
このセクションでは、エラーC2990が発生する具体的な背景について説明していきます。
エラーが発生する主な理由として、テンプレートやジェネリックの使用、ヘッダーファイルでの重複宣言、そしてCLR環境(Common Language Runtime)でのコンパイルオプションが絡むケースが挙げられます。
テンプレートクラスと非テンプレートクラスの重複
C++において、テンプレートクラスは柔軟な型定義を可能にしますが、その反面、同一の名前で非テンプレートクラスが定義されるとコンパイラが混乱します。
たとえば、次のようなコードがあった場合にエラーが発生します。
#include <iostream>
// テンプレートクラスの定義
template <class T>
class Sample {};
// 同じ名前での非テンプレートクラスの定義
class Sample {}; // ここでエラーC2990が発生
この例では、Sample
という名前が既にテンプレートとして定義されているため、同じ名前での非テンプレート定義が衝突し、エラーC2990となります。
ヘッダーファイルでの重複宣言
プロジェクトが大規模になると、複数のヘッダーファイル間で同じ型が誤って複数回定義される可能性が高まります。
ヘッダーファイルのインクルードガードや#pragma onceを使用している場合でも、異なる形態(テンプレートと非テンプレート)の定義が混在すると、C2990エラーが発生することがあります。
たとえば、あるヘッダーファイルAでテンプレートクラスMyClass
を定義し、ヘッダーファイルBで非テンプレートクラスとして同じ名前のMyClass
を定義していると、両者がリンクされる際にコンパイラが矛盾を検出します。
CLR環境下での発生要因
Visual Studioでは、/clrオプションを使用してCLRアプリケーションを作成することができます。
CLR環境下では、C++/CLIのジェネリッククラスが導入され、通常のC++テンプレートとは異なる扱いになります。
次のようなコードが例に挙げられます。
#include <iostream>
// CLR環境下でのジェネリッククラス定義
generic <class T>
ref struct GenericClass {};
// 同じ名前で非CLRクラスとして定義するとエラー発生
ref struct GenericClass {}; // エラーC2990が発生
この例では、GenericClass
がジェネリックとして定義されているにも関わらず、同一の名前で通常のクラスが定義されているため、CLR環境下でエラーが発生します。
CLRの場合、ジェネリッククラスの定義に対して厳密な型チェックが行われるため、重複定義は許容されません。
具体例による解説
ここでは、具体的なコード例を通して、エラーC2990がどのような状況で発生するのか解説します。
実際のソースコードとともに、どの部分がエラーの原因となっているかを明確にしていきます。
C++テンプレート利用時の事例
以下は、C++のテンプレートクラスを使用した場合のサンプルコードです。
テンプレートクラスと同じ名前で非テンプレートクラスを定義すると、エラーC2990が発生します。
#include <iostream>
// テンプレートクラスの定義
template <class T>
class MyTemplate {
public:
void display() {
std::cout << "テンプレートクラスの表示です" << std::endl;
}
};
// 同じ名前で非テンプレートクラスの定義
class MyTemplate { // ここでエラーC2990が発生
public:
void display() {
std::cout << "非テンプレートクラスの表示です" << std::endl;
}
};
int main() {
// 本来はどちらを使用するか明確にする必要がある
MyTemplate<int> objTemplate;
objTemplate.display();
return 0;
}
テンプレートクラスの表示です
この例では、MyTemplate
という名前が重複しているため、コンパイル時にエラーが発生します。
実際には重複定義を避けることが解決の鍵となります。
CLR(Generic)クラス使用時の事例
次に、CLR環境下でジェネリッククラスを利用する場合の例です。
CLRでは、generic
キーワードを使用してジェネリッククラスを定義しますが、同じ名前で通常のクラスが定義されるとエラーとなります。
#include <iostream>
// CLR環境下でのジェネリッククラス定義
generic <class T>
ref struct GC {
public:
void show() {
System::Console::WriteLine("CLRジェネリッククラスの表示です");
}
};
// 同じ名前での非ジェネリックな定義(エラーC2990が発生)
ref struct GC { // コンパイル時エラー
public:
void show() {
System::Console::WriteLine("非CLRジェネリッククラスの表示です");
}
};
int main() {
// CLRアプリケーションとして実行される場合の例
GC<int>^ gcObj = gcnew GC<int>();
gcObj->show();
return 0;
}
CLRジェネリッククラスの表示です
このコード例では、GC
という名前の定義が重複するため、コンパイラはエラーC2990を出力します。
CLR環境においては、クラス名の一貫性が特に重要であることがわかります。
コンパイル出力の確認方法
エラーが発生した際は、コンパイル出力ウィンドウを確認して、どの部分でエラーが発生しているかを特定することが大切です。
Visual Studioでは、エラーメッセージの詳細が表示されるため、以下の手順で確認できます。
- Visual Studioの「出力」ウィンドウを開く
- コンパイル時に表示されたエラーメッセージを確認する
- エラーメッセージに含まれるファイル名と行番号を基に、ソースコード内の重複定義部分を特定する
また、エラーメッセージに記載された内容を参考にして、該当箇所の定義を修正する必要があります。
特に、テンプレートと非テンプレート(またはCLRジェネリックと非ジェネリック)の定義が混在していないかを重点的にチェックすることが有効です。
エラー解消のポイント
エラーC2990を解消するためには、ソースコード内の重複した定義を見直すことが重要です。
以下のポイントに沿って、エラー解消のための手順を具体的に説明します。
重複宣言のチェック方法
エラーを回避するためには、まずどの部分で重複した宣言が行われているかを特定することが必要です。
主なチェック方法としては、以下の手順が推奨されます。
- 全てのヘッダーファイルを確認し、同一のクラス名が複数の形態で定義されていないかを調査する
- プロジェクト全体で、テンプレートクラスと非テンプレートクラス、またはCLRジェネリッククラスと非CLRクラスの定義を比較する
- 名前空間を適切に分けるなど、名前の衝突を防ぐための対策が行われているか確認する
これらの手順を実施することで、どの部分が問題となっているかを明確にすることができます。
コード修正とヘッダーファイルの整理方法
重複宣言が確認できた場合、具体的な修正作業に移ります。
修正のポイントは以下の通りです。
- 同じクラス名で異なる定義を行わないようにする。そのため、テンプレートクラスと非テンプレートクラスを明確に分ける
- ヘッダーファイルのインクルードガードや#pragma onceを用いて、不要な重複インクルードを防止する
- 名前空間を活用して、クラス名の衝突を回避する
- 必要な場合は、クラス名を変更することでエラーの回避を図る
たとえば、以下のようにヘッダーファイルを整理したコード例を示します。
// MyTemplate.h
#ifndef MYTEMPLATE_H
#define MYTEMPLATE_H
#include <iostream>
// テンプレートクラスの定義
template <class T>
class MyTemplate {
public:
void display() {
std::cout << "整理されたテンプレートクラスの表示です" << std::endl;
}
};
#endif // MYTEMPLATE_H
// main.cpp
#include <iostream>
#include "MyTemplate.h"
int main() {
// 修正後のクラスを使用
MyTemplate<int> obj;
obj.display();
return 0;
}
整理されたテンプレートクラスの表示です
この例では、ヘッダーファイルにインクルードガードを記述し、クラス名の重複が発生しないように工夫しています。
これにより、エラーC2990を回避できるようにしています。
まとめ
本記事では、エラーC2990の意味や定義、そしてテンプレートクラスやCLR環境下でのジェネリッククラスとの重複定義により発生する背景について解説しています。
具体例を通して、重複定義のチェックやヘッダーファイル整理など、エラー解消のためのポイントを示しました。
これらの内容を把握することで、コンパイルエラーの原因特定と解決に役立つ知識が得られるでしょう。