C言語のコンパイラエラー C2555について解説
この記事は、C言語の開発環境で発生するコンパイラ エラー C2555について解説します。
仮想関数を派生クラスでオーバーライドする際に、基底クラスで定義された戻り値の型と一致しない型を指定するとエラーが発生します。
解決のためには、戻り値の型を合わせるか、適切なキャストを行う方法があります。
エラー発生の原因と背景
C2555エラーは、仮想関数を派生クラスでオーバーライドする際に、戻り値型が基底クラスの定義と一致していない場合に発生します。
コンパイラは、戻り値の型が正しく共変(covariant)になっているかどうかをチェックし、一致しない場合にエラーを出力します。
以下では、仮想関数の基本仕様および、派生クラスにおける不一致事例について詳しく解説します。
仮想関数の基本仕様
仮想関数は、多態性を実現するために、基底クラスで宣言し、派生クラスでオーバーライドされる関数です。
オーバーライド時には、いくつかのルールが適用されます。
戻り値型の一致ルール
仮想関数をオーバーライドする際は、基底クラスの関数と戻り値型が同一である必要があります。
具体的には、以下のルールが適用されます。
- 基底クラスの関数とオーバーライドする関数は、戻り値型が全く同じでなければなりません。
- 特定の状況下では、共変戻り値と呼ばれる例外的なケースが認められますが、その場合でも型変換に関して厳密な制限があります。
たとえば、基底クラスの関数がvoid
型を返す場合、派生クラスは必ずvoid
型を返す必要があります。
戻り値型が異なる場合、コンパイラはエラーとして警告を出します。
共変戻り値の取り扱い
共変戻り値とは、オーバーライドする関数の戻り値型が、基底クラスの戻り値型と比べて派生型になっている場合のことです。
たとえば、基底クラスでBase*
を返す関数を、派生クラスでDerived*
を返すように定義することは可能です。
ただし、以下の点に注意が必要です。
- 共変戻り値は、ポインターまたは参照同士の場合にのみ適用されます。
- 型変換が自動では行われない場合、明示的なキャストが必要になるケースもあります。
- 基本ルールと異なり、参照とポインター間での自動変換は許されないため、型安全性が求められます。
派生クラスでの不一致事例
派生クラスで基底クラスの仮想関数をオーバーライドする際、戻り値型が不整合な場合にエラーが発生します。
ここでは、具体的な事例とコンパイラが検出する不整合について説明します。
異なる戻り値型定義の例
たとえば、基底クラスの仮想関数がvoid
型を返す場合に、派生クラスで異なる型(例:char
型)を返すように定義すると、戻り値型の不一致が発生します。
以下の例では、struct X
の関数func
はvoid
を返すように定義されていますが、struct Y
でこれをオーバーライドする際にchar
を返すようにしているため、エラーが起こります。
- 基底クラス: 戻り値型が
void
- 派生クラス: 戻り値型が
char
→ 戻り値型が一致していないため、共変戻り値として認められずエラーになります。
コンパイラが検出する不整合
コンパイラは、基底クラスと派生クラスでオーバーライドされる関数のシグネチャ―、特に戻り値型の整合性を厳密にチェックします。
エラー C2555 のメッセージには、
- オーバーライドされる関数の名前
- 戻り値型の不一致であること
- 共変性が適用されない旨
が示されます。
これにより、どの関数定義が原因で不整合が生じているのか、迅速に判断することが可能となります。
エラーメッセージの内容解説
C2555エラーのメッセージは、戻り値の型に関する不一致を明確に示してくれます。
ここでは、出力されるメッセージの具体例と、そこから読み取れるポイントについて解説します。
コンパイラからの出力例
コンパイラはエラー発生時に、以下のようなメッセージを出力します。
C2555エラーの具体例
実際のエラー出力例は以下のようになります。
- “class1::function1″: オーバーライドする仮想関数の戻り値の型が異なり、”class2::function2” の covariant ではありません
このメッセージは、どのクラスのどの関数でエラーが発生しているのか、そしてその原因が戻り値型の不一致であることを示しています。
メッセージから読み取るポイント
エラー出力例からは、次の点が読み取れます。
- 基底クラスの関数の戻り値型と、派生クラスの戻り値型が異なっていること
- 共変戻り値として認められない型変換が試みられていること
- 修正のためには、基底クラスの仕様に合わせた戻り値型の変更が必要であること
これにより、エラー発生箇所を特定し、適切な修正方法を考えるための情報が提供されます。
修正方法の解説
エラーを解消するためには、主に2つのアプローチがあります。
1つは、基底クラスの仕様に合わせた戻り値型の変更です。
もう1つは、適切なキャストを用いる方法です。
戻り値型を一致させる方法
正しいオーバーライドを実現するためには、まず基底クラスの仮想関数と派生クラスで戻り値型を一致させる必要があります。
基底クラス仕様に沿った修正
基底クラスの定義に合わせ、派生クラスのオーバーライド関数も同じ戻り値型に修正します。
たとえば、基底クラスの関数がvoid
型を返す場合、派生クラスもvoid
型で実装するように変更します。
- 基底クラス:
virtual void func()
- 派生クラス(修正前):
virtual char func()
- 派生クラス(修正後):
virtual void func()
このように修正することで、戻り値型の不一致が解消され、コンパイルエラーが防止されます。
キャストによる対処法
もう1つのアプローチは、基底クラスの仕様を維持しつつ、戻り値に対してキャストを適用する方法です。
キャスト適用時の注意点
キャストを用いる場合、以下の点に注意する必要があります。
- キャストはあくまで一時的な回避策であり、根本的な型安全性を損なう恐れがあります。
- 基底クラスの戻り値から派生クラスへのキャストは、実行時に問題が生じる可能性があるため、型変換が安全かどうかを十分に検証する必要があります。
- キャスト適用時は、明示的な型変換を行うことで、意図しない動作を防ぐためのチェックを追加するとよいでしょう。
これにより、キャストによって戻り値型の不一致を一時的に回避する方法もありますが、可能であれば基底クラスと派生クラスでの型定義を統一する方法が推奨されます。
実際のコード例による検証
ここでは、エラーが発生するコード例と、それを修正したコード例を具体的に示し、各修正箇所の比較と解説を行います。
エラー発生するコード例
以下のサンプルコードは、基底クラスと派生クラスで戻り値型が一致していないため、エラー C2555 が発生する例です。
なお、実際のコンパイルではエラーになるため、コンパイルや実行はできません。
#include <iostream>
using namespace std;
// 基底クラスX: 戻り値型がvoidの仮想関数を定義
struct X {
virtual void func() {
cout << "X::func called" << endl;
}
};
// 派生クラスY: 戻り値型をcharに変更してオーバーライド
struct Y : public X {
// 戻り値型がvoidと一致しないため、エラー C2555 が発生します
virtual char func() {
cout << "Y::func called" << endl;
return 'Y';
}
};
int main() {
// 基底クラスポインターで派生クラスのインスタンスを生成し呼び出す例
X* obj = new Y();
obj->func();
delete obj;
return 0;
}
(コンパイル時にエラー C2555 が発生し、実行はできません)
修正後のコード例
以下のコード例では、基底クラスの仕様に従い、派生クラスの戻り値型をvoid
に修正しています。
この変更により、エラー C2555 は解消されます。
#include <iostream>
using namespace std;
// 基底クラスX: 戻り値型がvoidの仮想関数を定義
struct X {
virtual void func() {
cout << "X::func called" << endl;
}
};
// 派生クラスY: 戻り値型をvoidに修正してオーバーライド
struct Y : public X {
// 基底クラスと一致する戻り値型を持つため、エラーは発生しません
virtual void func() {
cout << "Y::func called" << endl;
}
};
int main() {
// 基底クラスポインターで派生クラスのインスタンスを生成し呼び出す例
X* obj = new Y();
obj->func(); // Y::func が呼び出されます
delete obj;
return 0;
}
Y::func called
各修正箇所の比較と解説
- 変更前の派生クラス Y では、関数
func
の戻り値型がchar
となっており、基底クラス X のvoid
と不一致でした。 - 修正後は、Y における関数
func
の戻り値型をvoid
に統一し、基底クラスと同一のシグネチャにしています。 - この変更により、オーバーライド処理が正しく行われ、ランタイムで多態性が発現します。
以上の例から、C2555エラーが戻り値型の不一致によって発生すること、そして基底クラスの仕様に合わせて修正することでエラーが解消できることが理解できます。
まとめ
本記事では、C2555エラーの原因として、基底クラスの仮想関数と派生クラスのオーバーライド関数の戻り値型不一致が挙げられることを解説しています。
正しい共変戻り値の取り扱いや、型を一致させる修正方法、必要に応じたキャスト利用について具体例を交えて説明し、エラー解消のためのポイントを分かりやすく示しています。