C言語で発生するコンパイラエラー C2216 の原因と対策について解説
C言語の開発環境で発生するエラーC2216は、互いに排他的なキーワードが同じ文で使用された場合に出現します。
例えば、literalやexternなど、同時に使えないキーワードの誤った組み合わせが原因でコンパイルエラーとなります。
エラーメッセージをヒントにコードを見直すことで、問題箇所の特定と修正が可能です。
エラーC2216の概要
エラーC2216は、コンパイル時に互いに排他的な2つのキーワードが同時に使用された場合に発生するエラーです。
エラーメッセージには「’keyword1′ は ‘keyword2’ と共に使用できません」と表示され、どちらか一方を取り除く必要があることを示しています。
対象となるコードは主にC++/CLIの環境下で発生し、/clrオプションをつけた状況で確認されることが多いです。
エラーの発生条件
エラーC2216は、以下のような条件下で発生します。
- マネージドコードのコンパイル時に、互いに排他的なキーワードが同一の宣言内に存在する場合
- キーワードがそれぞれ別の役割を持っているため、コンパイラがどちらの動作を優先すべきか判断できない場合
たとえば、literalとstaticを同時に定義すると、コンパイラはどちらの意味を採用するか判断できずエラーになってしまいます。
実際のコード例でもこれが確認されており、特定のキーワードペアについては取り扱いが厳格に制限されています。
使用できないキーワードの組み合わせ
主に以下のキーワードの組み合わせがエラーC2216を引き起こします。
- literalと- static
→ ref structなどにおいて、literal修飾子を使用した定数メンバーに対してstaticを併用することが許容されない場合があります。
- externと- property
→プロパティに対してextern修飾子を付与することはサポートされておらず、エラーが発生します。
- typedefと- property
→プロパティ型の別名定義においても、typedefを用いることが禁止されています。
上記の組み合わせに該当する場合、どちらかのキーワードを取り除くか、設計を変更する必要があります。
原因の詳細解析
エラーC2216が発生する理由は、言語設計上、特定のキーワードの役割が明確に規定されているためです。
キーワード同士の排他関係が存在し、同時に利用することで意味論が矛盾する場合、コンパイラはエラーを返すことで設計上の不整合を防ぐ役割を果たしています。
排他的なキーワードについて
literal の役割と制限
literalキーワードは、コンパイル時に定数として扱われる値を示すために利用されます。
このキーワードは、主にマネージドな定数値の定義で使われ、実行時に変更できない値としてプログラム全体で共有されます。
しかし、literalには独自の取り扱いがあり、他の修飾子(たとえばstatic)と併せて使用すると、定数としての性格とクラスの静的メンバーとしての性格との間に矛盾が生じるため、エラーが発生します。
extern や typedef の使用制限
externは外部実体を参照するための修飾子で、プロパティやその他のメンバーと合わせると、外部リンケージの扱いが正しく行えなくなる可能性があります。
また、typedefは型の別名を定義するためのもので、これをプロパティに対して使用することは、構文上の制約により認められていません。
このように、言語仕様に基づく制限のため、これらのキーワードの組み合わせが禁止されている結果としてエラーC2216が発生するのです。
発生例に見るエラーの原因
実際のコード例を見ると、以下のような場合にエラーC2216が発生しています。
- literalと- staticが同時に用いられている例
→ コンパイラは定数と静的メンバーでの役割の違いを認識できず、どちらか一方に統一する必要があると判断します。
- externや- typedefがプロパティ定義に対して使用されている例
→プロパティに対して適用することが想定されていないため、エラーとなります。
このように、コード中で明示的に排他的な指定がなされている部分を見直すことで、エラーの根本原因を理解することが可能です。
実例によるコード解析
以下に示す各例では、実際にエラーC2216が発生する状況をサンプルコードとともに解説します。
例1: literal と static の組み合わせ
次の例は、literalとstaticを同時に使用した場合のコード例です。
#include <stdio.h>
// マネージドコードとして扱うためのヘッダ
#include <stdlib.h>
// C++/CLI環境を想定しているため、ref structを使用
ref struct Y1 {
    literal int staticConst2 = 10;   // literalとstaticの組み合わせによりエラーC2216が発生
};
int main(void) {
    // Y1の定数メンバーはクラス内の定義となっているため、直接アクセスはできない
    printf("Y1::staticConst2 の値: %d\n", Y1::staticConst2);
    return 0;
}// コンパイル時にエラーC2216が発生し、「literal は static と共に使用できません」というメッセージが表示されるこの例では、literalの特性上、staticと併用できないためエラーとなります。
例2: extern と property の組み合わせ
以下のコード例では、extern修飾子をプロパティに付与して使用しているためにエラーが発生します。
#include <stdio.h>
// マネージドコードのためのヘッダ
#include <stdlib.h>
// プロパティを含むクラスの定義
public ref class X {
public:
    extern property int i { int get(); }   // externはプロパティには使用できず、エラーC2216が発生
};
int main(void) {
    // クラスXのプロパティ利用を試みるが、コンパイルエラーにより実行に至らない
    return 0;
}// コンパイル時にエラーC2216が発生し、「extern は property と共に使用できません」というメッセージが表示されるこの例では、extern修飾子がプロパティに対して不適切に適用されているため、コンパイラがエラーを返します。
例3: インターフェイスと仮想関数の誤用例
以下の例では、インターフェイスと仮想関数の新たな定義方法との組み合わせにより、意図しないエラーが発生している状況を示します。
#include <stdio.h>
#include <stdlib.h>
// インターフェイスの定義
public interface struct I {
    double f();
    double g();
    double h();
};
// インターフェイスを実装する構造体Rの定義
public ref struct R : I {
    // virtual関数の定義において、new overrideを使用したためエラーが発生
    virtual double f() new override { return 0.0; }   // エラーC2216が発生
    virtual double g() new { return 0.0; }             // こちらはOKとされるケース
    virtual double h() override { return 0.0; }         // こちらもOK
};
int main(void) {
    R^ obj = gcnew R();
    printf("f(): %f\n", obj->f());
    printf("g(): %f\n", obj->g());
    printf("h(): %f\n", obj->h());
    return 0;
}// コンパイル時にエラーC2216がf()の定義部分で発生し、「virtualとnew overrideの組み合わせは許可されません」といった内容のエラーメッセージが表示されるこの例では、new overrideの組み合わせがエラーの原因となっているため、正しい仮想関数のオーバーライドの記述方法に修正する必要があります。
対策と修正方法
エラーC2216を回避するためには、排他的なキーワードの組み合わせを避け、設計や宣言の修正を行う必要があります。
以下に修正手法と注意点を説明します。
エラー回避のためのコード修正手法
エラーが発生する原因となるキーワードの組み合わせを取り除くため、以下の修正手順を採用してください。
- literalと- staticの組み合わせ
→ literalの利用目的に従い、必要な場合はstaticを外すか、設計の見直しを行う。
例:staticConst2をクラスの定数として定義するなら、literalのみを使用する。
- externもしくは- typedefと- propertyの組み合わせ
→プロパティの定義からこれらのキーワードを削除し、標準的なプロパティ定義に切り替える。
例:extern property int iを単にproperty int iと修正する。
- インターフェイスと仮想関数の組み合わせ
→ 仮想関数のオーバーライドにおいて、正しい構文(overrideのみまたはnewのみ)を利用する。
複数の修飾子を混在させないことが重要です。
修正後の確認方法と注意点
修正後のコードを確認する際、以下の点に注意してください。
- コンパイルオプションが正しく設定されているか確認する。特に、/clrオプションなどマネージドコードに関連するオプションが指定されているか注意が必要です。
- サンプルコードの各修正部分について、実際にコンパイルしエラーが解消されるかテストする。
- 修正したコードでも、設計上の意図する動作が正しく反映されているか、動作確認を行う。
- 複数のエラーが同時に発生している場合は、一つずつ解消する手法で問題の根本原因に着目する。
上記の対策を踏まえ、コードの見直しを行うことでエラーC2216を回避できるようになります。
まとめ
本記事では、C++/CLI環境で発生するエラーC2216について、発生条件や使用できないキーワードの組み合わせ、各例での原因を明確に解説しました。
literalやextern、typedefなどの特定キーワード同士の排他関係により起こるエラーについて、具体的なサンプルコードを交えてその原因と修正方法を示しました。
これにより、エラー発生時の対応策とコード修正のポイントが理解できます。
