コンパイラエラー

C言語のコンパイラエラー C2797 の原因と対策を解説

Microsoft C++コンパイラのC2797エラーは、Visual Studio 2013以前の環境で、メンバー初期化子リストや非静的データメンバー初期化子内のリスト初期化が実装されていないため発生します。

その結果、コードが暗黙の関数呼び出しに変換され、予期しない動作となる恐れがあります。

最新環境では改善されています。

エラーコード C2797 の概要

このエラーコードは、主に非静的データメンバーの初期化において、リスト初期化の機能が実装されていない環境で発生する現象です。

具体的には、メンバー初期化子リストや、クラス内での初期化子におけるリスト初期化の使用時に起こるエラーです。

エラーの発生状況

エラーコード C2797 は、Visual Studio 2013 Update 3 より前のコンパイラで発生する可能性があります。

たとえば、クラスや構造体のコンストラクタで、リスト初期化の記法を使用してメンバー変数を初期化する際に、意図した挙動にならず、暗黙の変換が行われるケースがあります。

その結果、コンパイラは「メンバー初期化子のリストまたは非静的データ メンバーの初期化子内のリストの初期化は実装されていません」というエラーメッセージを出力することとなります。

エラーメッセージの特徴

エラーメッセージには以下のような特徴があります。

  • 「リスト初期化」と「初期化子リスト」という用語が含まれ、具体的にどの初期化が問題となっているかを示します。
  • エラーメッセージ自体は、実装上の未対応事項を指摘しており、利用者の書いたコードが意図する挙動とコンパイラの実装との差異を明示する形になっています。
  • このエラーは、主に Visual Studio 2013 以前のバージョンで発生し、Visual Studio 2015以降では対応済みとなるケースが多いことが特徴です。

発生原因の詳細

エラーの根本原因は、コンパイラにおけるリスト初期化の実装が完全ではなく、暗黙の型変換や関数呼び出しへの変換が行われる点にあります。

以下に、具体的な要因を解説します。

メンバー初期化子リストの実装状況

メンバー初期化子リスト内でリスト初期化を使用する記法は、C++11で導入された記法ですが、古いVisual Studioのコンパイラ(特に Visual Studio 2013 Update 3 より前)では、この機能が完全には実装されていません。

そのため、リスト初期化をそのまま記述すると、コンパイラが暗黙に関数呼び出しの形式に変換してしまい、意図しない挙動やエラー C2797が発生することになります。

非静的データメンバー初期化子内リスト初期化の問題点

クラス内で直接非静的データメンバーの初期化をする場合や、構造体での初期化子を使用すると、リスト初期化が内部的に扱われる仕組みになっています。

しかし、これもまたコンパイラ側の実装依存の挙動になっており、暗黙の型変換が発生することでエラーが生じる可能性があります。

たとえば、std::vectorの初期化において {1} と記述しても、コンパイラがそれをサイズ指定のコンストラクタ呼び出しに誤って変換してしまう場合などが挙げられます。

Visual Studioバージョン間の差異

Visual Studio 2013以前のバージョンでは、メンバー初期化子リスト内および非静的データメンバー初期化子内でのリスト初期化が十分にサポートされていませんでした。

Visual Studio 2013 Update 3以降、またはVisual Studio 2015以降のバージョンでは、この問題に対するエラー報告が改善され、正しく実装されるようになっています。

そのため、開発環境やプロジェクトのコンパイラバージョンに注意しながら、コードの記述方法を選択する必要があります。

コード例による現象の解説

エラー発生の例として、実際のコード例を交えながら、どのような状況で C2797 エラーが生じるのかを考察します。

エラーを引き起こすコード例

コンパイル時にエラーが発生する可能性があるコード例を以下に示します。

これらのサンプルコードは、リスト初期化が正しく解釈されず、エラー C2797 が発生する例となります。

Vector初期化時の問題

以下のサンプルコードでは、std::vectorのメンバー変数をリスト初期化しようとする記述が原因でエラーが発生する可能性があります。

#include <vector>
#include <iostream>
using namespace std;
// 構造体 S のコンストラクタ内で vector のリスト初期化を行う例
struct S {
    S() : v1{1} {  // ここでエラー C2797 が発生する可能性がある
        // 一部のコンパイラでは、{1} を vector(size_type) 呼び出しに暗黙変換する
    }
    vector<int> v1;
};
int main() {
    S s;
    cout << "v1の要素数: " << s.v1.size() << endl;
    return 0;
}
v1の要素数: 1

構造体メンバー初期化のトラブル

次に、複数の構造体メンバーのリスト初期化によりエラーが発生する例です。

以下のコードでは、構造体 S1S2 を用いて、各メンバーの初期化時に C2797 エラーが発生する可能性があります。

#include <iostream>
#include <string>
using namespace std;
struct S1 {
    int i;
};
struct S2 {
    S2() : s1{0} {   // ここでエラー C2797 が発生する場合があります
        // s1{0} は誤って s1(0) と解釈される可能性がある
    }
    S1 s1;
    S1 s2{0};  // この初期化でも同様の問題が生じるケースがある
};
int main() {
    S2 s;
    cout << "s1.i: " << s.s1.i << ", s2.i: " << s.s2.i << endl;
    return 0;
}
s1.i: 0, s2.i: 0

内部リスト初期化の誤った変換

上記のコード例から分かるように、リスト初期化をそのまま使用すると、コンパイラは {} 内の初期化子をリストとして正しく扱えず、暗黙的に関数呼び出し形式に変換してしまう場合があります。

たとえば、std::vector<int> v1{1}; と記述した場合、本来は要素数1のベクターを初期化する意図でも、コンパイラはそれを vector<int>(1) と読み替えてしまう可能性があるため、注意が必要です。

この誤った変換がエラー C2797 の原因となり、意図した動作と異なるコードが生成される状況が発生します。

エラー対策と修正方法

C2797 エラーを回避するためには、リスト初期化の利用方法を明示的に記述することで、コンパイラに正しい解釈を促す方法が有効です。

明示的な初期化方法の選択

リスト初期化に代わり、明示的なコンストラクタ呼び出しを使用することが推奨されます。

これにより、コンパイラは暗黙の型変換ではなく、明示的な呼び出しとしてコードを解釈するため、意図した動作が確実に実現されます。

たとえば、std::vector<int>の初期化については、以下のように書き換えることが考えられます。

修正コード例の構造

以下のサンプルコードは、リスト初期化によるエラー C2797 を回避するために、明示的な初期化を行う例です。

#include <vector>
#include <iostream>
using namespace std;
// typedef を利用して Vector 型を定義しています
typedef vector<int> Vector;
struct S {
    S() : v1(Vector{1}) {  // 明示的に Vector{1} を指定することでエラーを回避
    }
    Vector v1;
    Vector v2 = Vector{1, 2};  // こちらも同様に明示的な初期化を行っています
};
int main() {
    S s;
    cout << "v1の要素数: " << s.v1.size() << endl;
    cout << "v2の要素数: " << s.v2.size() << endl;
    return 0;
}
v1の要素数: 1
v2の要素数: 2

注意すべきポイント

リスト初期化と明示的な初期化の違いを理解することが重要です。

明示的な初期化が必要な状況としては、以下の点が挙げられます。

  • コンパイラのバージョンにより、リスト初期化の実装が不完全な場合があること
  • 意図しない暗黙変換が発生し、関数呼び出し形式に変換される可能性があること
  • 複数の初期化子がある場合、特に構造体やクラス内の複数メンバーの初期化においては、各初期化が個別に正しく解釈されるよう明示的に記述する必要があること

これらの注意点を踏まえ、各メンバーごとに適切な初期化方法を選択することで、エラー C2797 の回避と意図通りのコード実行が可能となります。

まとめ

この記事では、C2797 エラーの発生原因とその背景にあるリスト初期化の問題について解説しました。

古いVisual Studio環境では、リスト初期化が正しく実装されず、暗黙変換により関数呼び出しへ変換されることでエラーが出ることが分かります。

明示的な初期化方法を用いることで、これらの問題を回避できることを確認できました。

関連記事

Back to top button
目次へ