【C言語】コンパイラエラー C2695について解説
この記事では、Microsoft Visual C++で発生するエラーC2695について説明します。
エラーは、派生クラスで基底クラスの仮想関数をオーバーライドする際、呼び出し規約が異なる場合に発生します。
例えば、基底クラスが__fastcall
を使用しているときに、派生クラスが__clrcall
を使用するといったケースが該当します。
エラーの原因や対策を簡潔に理解できる内容となっています。
エラー C2695の原因
C2695 エラーは、派生クラスの仮想関数が基底クラスの関数をオーバーライドする際に、呼び出し規約が異なる場合に発生します。
エラーメッセージには、オーバーライド対象の関数と異なる呼び出し規約が原因である旨が記載されます。
派生クラスの関数が基底クラスの関数と同じシグネチャを持たなければならないため、呼び出し規約が一致していないとエラーが表示されるのです。
エラーメッセージの内容解析
エラーメッセージには「オーバーライドする仮想関数と呼び出し規約のみが異なっています」といった記述があります。
これは、関数シグネチャの中でも返り値や引数だけでなく、呼び出し規約も含まれるという重要なポイントを示しています。
コンパイラは、基底クラスの仮想関数と派生クラスのオーバーライド関数で呼び出し規約が同一でない場合、正しくオーバーライドされないと判断します。
呼び出し規約の不整合
呼び出し規約とは、関数呼び出し時に引数や戻り値がどのように扱われるかを決めるルールです。
基底クラスと派生クラスで異なる呼び出し規約を指定すると、コンパイラは両者を同一視できず、結果としてオーバーライドが成立しません。
そのため、呼び出し規約に不整合があるとエラー C2695 が発生します。
__fastcall と __clrcall の違い
C++ では、__fastcall
と __clrcall
といった異なる呼び出し規約が利用できます。
・__fastcall
は、引数をレジスタ経由で渡すため高速な呼び出しが可能な規約です。
・__clrcall
は、管理対象のコード(例えば、.NET 環境)向けの呼び出し規約であり、呼び出し方法が異なります。
これらの違いにより、仮想関数オーバーライド時に基底クラスと派生クラスで一方が __fastcall
、もう一方が __clrcall
だと、シグネチャが異なると判断され、エラーが発生します。
呼び出し規約の基本知識
呼び出し規約の定義
呼び出し規約は、関数が呼び出される際のスタック操作や引数の受け渡し方法、戻り値の処理方法などを定義するルールです。
これにより、関数間で整合性のある呼び出しが行われ、実行時の挙動が保証されます。
関数シグネチャの一部として呼び出し規約が含まれるため、オーバーライド時は基底クラスと同じ規約を使用する必要があります。
C++における仮想関数の挙動
C++ では、仮想関数が持つ動的バインディング機能により、実行時に適切な関数が呼び出されます。
仮想関数は、仮想関数テーブル(vtable)を利用して、オブジェクトがどの関数を呼び出すかを決定します。
正しくオーバーライドされない場合、意図しない関数が呼び出される可能性があります。
基底クラスと派生クラスの関係
基底クラスで定義された仮想関数は、派生クラスでオーバーライドする際に必ず同じシグネチャで定義する必要があります。
これは、呼び出し規約も含めた関数の全体の一致を要求されるため、呼び出し規約が変更されると正しいオーバーライドとは見なされません。
基底クラスと派生クラス間の一貫性が、正しい動的バインディングの実現に不可欠です。
仮想関数オーバーライド時の注意点
シグネチャの一貫性の必要性
仮想関数をオーバーライドする際は、戻り値、引数、修飾子に加えて呼び出し規約まで全て一致する必要があります。
シグネチャの僅かな違いでも、コンパイラはオーバーライドと認識せず、エラーを発生させるため注意が必要です。
関数オーバーライドの仕様確認
関数をオーバーライドする際は、基底クラスの宣言を正確に確認し、その仕様に合わせた定義を行うことが重要です。
特に、コンパイラによっては警告が出る場合もあるため、呼び出し規約の宣言を見落とさずに確認する必要があります。
エラー対策の具体的手順
呼び出し規約の統一方法
エラー C2695 を解消するためには、基底クラスと派生クラスで同じ呼び出し規約を使用する必要があります。
コード全体を見直し、オーバーライドする関数の呼び出し規約が一致しているかを確認してください。
必要であれば、基底クラスまたは派生クラスどちらかの修正を行います。
コード修正の実例
以下のサンプルコードは、呼び出し規約の不整合が原因でエラーが発生する場合の例と、その修正方法を示しています。
#include <iostream>
// 基底クラスの宣言
class Base {
public:
// __fastcall を指定した仮想関数
virtual void __fastcall func() {
std::cout << "Base func using __fastcall" << std::endl;
}
};
// 派生クラスの宣言
class Derived : public Base {
public:
// 呼び出し規約が __fastcall で統一されているため、正しくオーバーライドされる
virtual void __fastcall func() override {
std::cout << "Derived func using __fastcall" << std::endl;
}
};
int main() {
// 仮想関数を利用した動的バインディングの検証
Base* obj = new Derived();
obj->func(); // "Derived func using __fastcall" と出力される
delete obj;
return 0;
}
Derived func using __fastcall
修正後の検証手順
修正後は、コンパイル環境でコードをビルドしてエラーが解消されていることを確認してください。
修正箇所の呼び出し規約が一致している場合、正しくオーバーライドが実現され、プログラム実行時に期待通りの動作となります。
公式情報と参考資料
Microsoft Learn の解説内容
Microsoft Learn に掲載されている解説では、オーバーライドする仮想関数とその基底クラスで、呼び出し規約のみの違いの場合にエラー C2695 が発生する旨が説明されていました。
派生クラスで呼び出し規約を変更することは不許可となっているため、修正が必要である点が強調されています。
関連エラーとの比較情報
他にも、関数シグネチャの不一致に起因するエラーが存在します。
例えば、関数の戻り値や引数の型に違いがある場合も、オーバーライドが正しく機能しません。
エラー C2695 は特に呼び出し規約に焦点を当てたエラーであるため、その他のシグネチャの不一致エラーと区別して理解することが重要です。
まとめ
この記事では、エラー C2695 の原因が、基底クラスと派生クラスの仮想関数間で呼び出し規約が不一致であることに起因する点を解説しています。
呼び出し規約の定義や、__fastcall
と __clrcall
の違い、正しいオーバーライドのためのシグネチャの一致の重要性について説明しました。
さらに、具体的なコード修正方法と検証手順を提示し、Microsoft Learn での公式解説との関連性についても触れています。