コンパイラエラー

C言語で発生するコンパイラ エラー C3364の原因と対策について解説

コンパイラ エラー C3364は、Microsoft C++/CLI環境で発生し、delegateコンストラクターに渡す引数が正しい関数ポインター(マネージドクラスのメンバー関数やグローバル関数のアドレス)でない場合に表示されます。

C言語やC++の開発時に、正しい指定を確認することで対処できます。

エラー C3364の概要

このエラーは、C++/CLI環境でデリゲートのコンストラクターを使用する際に発生する代表的なエラーのひとつです。

エラーメッセージには「delegate コンストラクター: 引数は、マネージドクラスのメンバー関数またはグローバル関数へのポインターである必要があります」と記載されており、デリゲート生成時に指定する関数の指定方法が正しくない場合に表示されます。

エラーメッセージの詳細

エラーメッセージは以下のようになっています。

「delegate コンストラクター: 引数は、マネージドクラスのメンバー関数またはグローバル関数へのポインターである必要があります」

この内容は、デリゲートのコンストラクターの第2パラメーターとして、関数ポインターそのものを渡す必要があることを示しています。

たとえば、関数の呼び出し結果を渡すとコンパイラはこれを関数ポインターと解釈できず、エラー C3364 を発生させます。

発生背景の確認

このエラーは、C++/CLIでマネージドコードを扱う際に、デリゲートの構築方法に不備があるときに発生します。

典型的な例として、次のようなコードがあります。

#include <cliext/adapter>
#include <System.Console.h>
delegate int D(int, int);
ref class C {
public:
    int mf(int a, int b) {
        return a + b;
    }
};
int main() {
    C^ pC = gcnew C;
    // 間違った関数呼び出しとして、結果を渡してしまっている例
    System::Delegate^ pD = gcnew D(pC, pC->mf(1, 2));  // エラー C3364 が発生する
    return 0;
}

ここでは、pC->mf(1, 2)のように関数呼び出しの結果が渡されているため、正しい関数ポインターとして認識されずエラーとなります。

エラー原因の詳細

delegateコンストラクターの要求仕様

C++/CLIにおいて、デリゲートのコンストラクターは第1パラメーターに対象のオブジェクト、第2パラメーターに関数ポインターを受け取るように設計されています。

すなわち、メソッドの実体を呼び出すのではなく、メソッド自体のアドレスを渡す必要があるためです。

関数ポインターの正しい指定方法

正しい指定方法としては、メンバー関数に対してはアドレス演算子(&)を用いて、以下のように渡します。

// 正しい指定方法の例
System::Delegate^ pD = gcnew D(pC, &C::mf);

このようにすることで、C::mfというメンバー関数のアドレスが正しく取得され、デリゲート内で利用することが可能になります。

メンバー関数と静的関数の違い

メンバー関数と静的関数では、関数呼び出しの際の取り扱いが異なります。

・メンバー関数の場合

インスタンスに紐付けられており、関数ポインターの指定は&クラス名::メソッド名となります。

・静的関数の場合

クラスに所属するものの、インスタンスを必要としないため、同様に&クラス名::StaticMethodとして指定できますが、インスタンスの参照は不要になります。

この違いを理解しないと、誤った関数呼び出しやポインターの指定につながり、エラー C3364 の原因となることがあります。

誤った引数指定の事例

誤例コードの分析

一般的なミスは、デリゲートコンストラクターに対して、関数ポインターではなく関数呼び出しの結果を渡してしまうことです。

次のコードは誤った記述例です。

#include <cliext/adapter>
#include <System.Console.h>
delegate int D(int, int);
ref class C {
public:
    int mf(int a, int b) {
        return a + b;
    }
};
int main() {
    C^ pC = gcnew C;
    // 間違った例:関数呼び出しの戻り値(int型)を渡してしまうためエラーが発生する
    System::Delegate^ pD = gcnew D(pC, pC->mf(1, 2));
    return 0;
}

このコードでは、pC->mf(1, 2)が実行され、その結果(整数値)が渡されるため、コンパイラはこれを関数ポインターと解釈できません。

正しくは、関数自体のアドレスを指定する必要があります。

エラー解決と対策

正しい関数ポインターの指定方法

エラーを回避するためには、関数の呼び出し結果ではなく、正しく関数ポインターを指定してデリゲートを生成する必要があります。

これにより、コンパイラはデリゲートの第2パラメーターが正しい関数のアドレスであると認識します。

修正手順と具体的コード例

以下は、誤ったコードを修正した例です。

コード内のコメントでわかりやすく解説しています。

#include <cliext/adapter>
#include <System.Console.h>  // Console.WriteLine の利用のため
delegate int D(int, int);
ref class C {
public:
    int mf(int a, int b) {
        // 単純な和を返す
        return a + b;
    }
};
int main() {
    // C クラスのオブジェクトを作成
    C^ instance = gcnew C;
    // 正しい関数ポインターを指定してデリゲートを生成する
    System::Delegate^ delegateInstance = gcnew D(instance, &C::mf);
    // dynamic_cast を用いてデリゲートを特定の型に変換
    int result = dynamic_cast<D^>(delegateInstance)->Invoke(3, 4);
    // 結果を出力する
    System::Console::WriteLine(result);
    return 0;
}
7

この例では、&C::mfにより関数ポインターを正しく取得し、Invokeメソッドによってデリゲート関数が呼び出され、期待した結果が表示されます。

C++/CLI環境での注意点

マネージドクラス利用時のポイント

C++/CLIでデリゲートを使用する際には以下の点に留意する必要があります。

・デリゲート生成時に渡す第1パラメーターは、対象となるマネージドクラスのインスタンスである必要があります。

・第2パラメーターには、関数呼び出しではなく、関数のアドレス(ポインター)を必ず指定する必要があります。

・静的メンバー関数の場合は、インスタンスを必要としないため、呼び出し方法が異なることを認識してください。

・コンパイルオプションに/clrが指定されているか確認してください。

C++/CLIコードはこのオプションが有効な環境でコンパイルされることが前提です。

以上のポイントに注意することで、エラー C3364 の発生を防ぎ、正しくデリゲートを利用できるようになります。

まとめ

本記事では、C++/CLI環境で発生するコンパイラ エラー C3364 の原因と対策について解説しています。

エラーが起こる主な理由は、デリゲート生成時に関数呼び出し結果ではなく、正しい関数アドレスを渡す必要がある点です。

具体例を通して、修正手順やメンバー関数と静的関数の違い、さらにマネージドクラス使用時の留意点を学ぶことができます。

関連記事

Back to top button
目次へ