C++/CLIは、Microsoftが
C++を拡張した
プログラミング言語であり、
.NET Frameworkの
共通言語基盤(CLI)に対応し、
共通言語ランタイム(CLR)上で実行されるプログラムを記述するために使用されます。前身である
C++マネージ拡張に比べて、構文が単純化され
可読性が向上しています。
Microsoft Visual Studio 2005からサポートが開始されました。
標準化とコンパイラ
C++/CLIの最初のバージョンは、Ecma Internationalで標準化されています。対応するコンパイラとして、Microsoft Visual
C++ 2005 (8.0) 以降が存在します。また、
Clang上での実装も試みられています。
対応プラットフォーム
当初は
.NET Frameworkでのみ利用可能でしたが、
.NET Core 3.1およびVisual Studio 2019 (16.4) から
.NETでもサポートされるようになりました。ただし、対応プラットフォームは
Microsoft Windowsに限られています。
構文の変化
C++マネージ拡張が
C++のスーパーセットであったのに対し、
C++/CLIは独立した言語として設計されています。これにより、曖昧な識別子の削除や
.NET固有の機能追加など、大きな構文上の変更が行われました。
最も大きな変更点の一つは`new`演算子です。
C++/CLIでは、
.NETの参照型の
インスタンスを生成するために`gcnew`演算子が導入されました。また、
.NET 2.0のジェネリクスに対応するための構文も追加されています。
C++/CLIは標準
C++と概ね互換性がありますが、
C++11で追加された`
`などの一部の標準ライブラリを使用できない問題がありました。Visual Studio 2022 (17.6) ではC++/CLIモードがC++20モードと併用可能になり、標準C++との互換性が改善されました。
新しい構文機能やキーワードの多くはC#の影響を受けています。`nullptr`、`override`、`enum class`、委譲コンストラクタなど、C++/CLIの独自拡張機能の中には、後に標準C++に取り込まれたものもあります。ただし、`enum class`はC++/CLIと標準C++で互換性がないため、C++/CLIコードの修正が必要になる場合があります。
Microsoft Windows 8で導入されたWindowsランタイム(WinRT)を利用するコードをC++で効率的に記述するために、Visual Studio 2012ではC++/CXが導入されました。C++/CXはC++/CLIとよく似た構文を採用していますが、C++/CLIがマネージ言語拡張であるのに対し、C++/CXはネイティブ言語拡張です。
用途
C++/CLIは、C++マネージ拡張と同様に、マネージコードとネイティブコードを混在して記述できる唯一の.NET言語です。主に、C言語やC++で記述されたネイティブコードを、C#やVisual Basic .NET (VB.NET) などのマネージ言語から利用するために使用されます。また、マネージ言語で記述されたコードをC++から利用するための混在コードを記述することも可能です。
.NETにはP/InvokeやCOM相互運用などの手段もありますが、C++/CLIでネイティブコードをラップしてマネージアセンブリ(DLL)を作成する方法は、より細やかな制御が可能であり、.NET言語から直接扱いやすいプログラミングインターフェイスを提供できます。
一方で、.NETアプリケーションコードの効率的な記述能力やRAD(迅速なアプリケーション開発)の面ではC#やVB.NETに劣り、IDE(統合開発環境)の支援を受けられないこともあります。Visual Studio 2010まではWindows Forms関連のプロジェクトテンプレートが提供されていましたが、Visual Studio 2012では削除されました。WPF(Windows Presentation Foundation)関連のプロジェクトテンプレートは最初からサポートされていません。C++/CLIは、マネージコードとアンマネージコードの相互運用を目的として使用することが推奨されています。
ハンドル
従来のマネージ拡張C++には、`__nogc`ポインタと`__gc`ポインタの2種類が存在しました。C++/CLIでは、.NETの参照型のオブジェクトを指すものを「ハンドル」と呼称し、`クラス名^`という構文を使用します。これにより、ガベージコレクションされるオブジェクトとそうでないものが明確になり、コードの可読性が向上しました。`gcnew`はC#の`new`に相当します。ハンドルからメソッドやプロパティへのアクセスにはアロー演算子 `->` を使用します。
追跡参照
C++/CLIの追跡参照(トラッキング参照)は、値ではなく参照で渡されるハンドルです。C#の`ref`やVB.NETの`ByRef`に相当し、`^%`という構文を使用します。これにより、関数内で参照先の値を変更することが可能になります。
例えば、`String^% s`のように追跡参照を使用した場合、配列の要素への変更が関数内で有効になります。`String^ s`のように値を渡してしまうと、コピーが作成されてしまい、配列の要素は変更されません。
C#の`foreach`文ではコレクション要素を参照として取得できないため、同様の処理を実現するには回避策が必要です。
C++/CLIには、C#の`out`パラメータ修飾子に相当する構文はありませんが、属性構文`[System::Runtime::InteropServices::Out]`を使用してメソッド引数を修飾することで、同様の動作を実現できます。
ファイナライザと自動変数
C++/CLIでは、ガベージコレクション時に実行されるファイナライザの構文が`!クラス名()`に変更されました。デストラクタは`~クラス名()`となり、従来のC++と同じ意味を持ちます。デストラクタは自動的に呼び出され、CIL(共通中間言語)上ではIDisposableインターフェイスのDisposeメソッドとして実装されます。これにより、C++/CLIでもRAII(Resource Acquisition Is Initialization)が可能です。
演算子の多重定義
C++の演算子の多重定義は、C++/CLIでも概ねそのまま利用できます。すべての``は`^`に、すべての`&`は`%`に置き換わりますが、それ以外の構文はそのまま使用可能です。さらに、クラス自身だけでなく、クラスへのハンドルに対しても演算子の多重定義が可能です。これは、従来のC++ではポインタ型同士では多重定義できなかったことです。CLIに適合するため、演算子の多重定義はクラスの静的メンバとして実装することも可能です。.NET Frameworkの参照クラスでも、ハンドルを引数に取る演算子の多重定義は静的メンバとして実装されています。
これにより、文字列が同じであれば、異なるStringの参照を`==`演算子で比較しても、`String`の`==`演算子の多重定義によって結果が`true`となることがあります。ただし、演算子の多重定義は多態的ではないため、`Object^`へのキャストによって多重定義のセマンティクスを回避できます。
標準的なセマンティクスでは、ネイティブ型や値型に対しては従来のC++のように`T`や`T const&`を引数に取る演算子を定義し、参照クラス型`R`に対してはハンドル`R^`を引数に取る演算子を定義します。しかし、C++だけのプロジェクトでは、ハンドル型を引数に取る演算子の多重定義を使わず、参照クラスに対しても`R const&`を引数に取ることも可能です。このような手法は、コピーコンストラクタや代入演算子などで利用されることがあります。
脚注
C++/CLIに関する注釈や出典、関連情報については、以下の項目を参照してください。
C++
C++マネージ拡張
Microsoft Visual C++
C#
.NET Framework
共通言語基盤 (CLI)
外部リンク
ECMA-372 - Ecma International
C++: .NET Framework プログラミング最良の言語
変換ガイド: Managed Extensions for C++ から C++/CLI へのプログラムの移行
* 新しい言語デザインの勧め - MSDN ライブラリ