Standard
ECMA-372
C++/CLI 言語仕様書
西暦2005年12月 初版
ナビゲーション リンクのスキップ

15.演算

 型 long long int と型 unsigned long long int、そして、拡張整数型を協調的に追加するために、標準C++(§5/9 )は次のように変更されます。
 算術オペランドや列挙型を期待する多くの二項演算子は似たような方法で変換を引き起こし、返却型を生み出します。 その目的は共通型を譲渡するためで、それはまた結果の型です。 このパターンは通常算術変換が呼び出されます。それは次のように定義されます。
――もし、どちらかのオペランドが long double 型のものであれば、他方は long double に変換されるべきです。
――そうでなければ、もし、どちらかのオペランドが double であれば、他方は double に変換されるべきです。
――そうでなければ、もし、どちらかのオペランドが float であれば、他方は float に変換されるべきです。
――そうでなければ、整数促進(4.5)が双方のオペランドに実行されるべきです。
――その時、もし、どちらかのオペランドが unsigned long であれば、他方も unsigned long に変換されるべきです。
――そうでなければ、もし、あるオペランドが long int でもう一方が unsigned int であれば、その時、もし、long int が unsigned int の全ての値を表現できるのであれば、 その unsigned int は long int に変換されるべきです。そうでなければ、双方のオペランドは unsigned long int に変換されるべきです。
――そうでなければ、もし、どちらかのオペランドが long であれば、他方は long に変換されるべきです。
――そうでなければ、もし、どちらかのオペランドが unsigned であれば、他方は unsigned に変換されるべきです。
[注意:そうでなければ、ただ残った場合は双方とも int となります。]
――そうでなければ、双方のオペランドに整数促進が実行されます。その時、促進されたオペランドには次のルールが適用されます。
――もし、双方のオペランドが同じ型を持っていたら、その時それ以上の変換は不要です。
――そうでなければ、もし、双方のオペランドが符号付き整数型であるか、双方が符号無し整数型であれば、その低い整数変換ラックを持つ型のオペランドは高いランクを持つオペランドの型に変換されます。
――そうでなければ、もし、符号無し整数型が他のオペランドの型のランク以上のランクを持っていれば、その時、符号付き整数型であるオペランドは符号無し整数型のオペランドの型に変換されます。
――そうでなければ、もし符号付き整数型のオペランドの型が符号無し整数型のオペランドの型の全ての値を表現できるのであれば、その時、符号無し整数型のオペランドは符号付き整数型のオペランドの型に変換されます。
――そうでなければ、双方のオペランドは符号付き整数型のオペランドの型に対応した符号無し整数型に変換されます。

15.1 関数メンバ

 次の関数メンバ種類が標準 C++ で定義されているものに追加されます。
  • プロパティ(スカラーとデフォルト・インデックス化の双方)
  • イベント
 これらのメンバ関数が含まれる命令文は関数メンバ呼び出しを通して実行されます。 関数メンバ呼び出しを書くための実際の構文は特定の関数メンバのカテゴリに依存します。
 デフォルト・インデックス化プロパティの呼び出しは関数メンバを呼び出す候補セットの決定のために多重定義解決を使います。
[注意:以下の表は明示的に呼ぶことができる関数メンバのこれら三つのカテゴリを含んだ生成中に起こる過程をサマリー化しています。 表中の e, x, y、そして、value は変数や値として分類された演算を示し、E はイベント、P はプロパティの単純名です。
生成記述
プロパティ・アクセスPP::get()
P = valueP::set(value)
イベント・アクセスE += valueE::add(value)
E -= valueE::remove(value)
デフォルト・インデックス化プロパティ・アクセスe[x,y]e.default::get(x,y)
e[x,y] = valuee.default::set(x,y,value)


15.2 基本演算

 プロパティの追加を協調的に行うために、標準C++(§5.1 )の項「基本演算」は以下のように拡張されます。
”静的プロパティや静的イベントはクラスのインスタンスには関連づけられておらず、もし、静的プロパティやイベントのアクセサ関数の中で this を参照していれば、そのプログラムは不正です。”
”インスタンス・プロパティやインスタンス・イベントはクラスの特定のインスタンスに関連しており、そのインスタンスはインスタンス・プロパティやイベントのアクセサ関数中で this を参照することができます。”

15.3 後置演算

 デフォルト・インデックス化プロパティと CLI 配列(それらは配列演算を使ってアクセスするので)の追加を協調的に行うために、postfix-expression(後置演算)のための標準 C++ の文法(§5.2 )は以下のように拡張されます。
postfix-expression:
  primary-expression
  postfix-expression [ expression-list ]
  postfix-expression ( expression-listopt )
  simple-type-specifier ( expression-listopt )
  typename ::opt nested-name-specifier identifier ( expression-listopt )
  typename ::opt nested-name-specifier templateopt template-id ( expression-listopt )
  postfix-expression . templateopt id-expression
  postfix-expression -> templateopt id-expression
  postfix-expression . pseudo-destructor-name
  postfix-expression -> pseudo-destructor-name
  postfix-expression ++
  postfix-expression --
  dynamic_cast < type-id >( expression )
  static_cast < type-id >( expression )
  reinterpret_cast < type-id >( expression )
  const_cast < type-id >( expression )
  typeid ( expression )
  typeid ( type-id )
  typenameopt ::opt nested-name-specifier identifier :: typeid
  typenameopt ::opt nested-name-specifier templateopt template-id :: typeid
 標準C++ の成果、
postfix-expression [ expression ]
は、
posifix-expression  [  expression-list  ] 
のようにインデックス化アクセス(§15.3.1 )と CLI 配列要素アクセス(§24.3 )を協調させるために変更されています。 結果として、角括弧演算中のコンマは演算子ではなく、代わりにリスト分割子となります。
 List<List<int>> のような、>> が一つではなく二つのトークンとして扱われるような、生成物を許すために、標準C++(§5.2/2 )に次のような新しいパラグラフが追加されます。
[注意:const_cast、dynamic_cast、reinterpret_cast、safe_cast、ないし、static_cast 中に type-id が続く > トークンは、>> トークンが二つの連続的な > トークン(14.2 )によって置き換えられた結果かもしれません。]

15.3.1 配列記号とインデックス・アクセス

 配列演算子 [] はビルトイン配列演算(標準 C++ §5.2.1 )、多重定義 operator [] (標準C++ §13.5.5 )の呼び出し、ないし、インデックス化プロパティの使用を表現することができます。 多重定義解決はどれが適用されるのかを決定するために使われます。標準 C++ と同様に、もし、オペランドがクラスでも列挙でもクラスへのハンドルでもなければ、多重定義解決は必要とされず、ビルトイン演算子が選択されます。
 与えられた任意の ref クラスのインスタンスに、配列記号はインスタンスとそのインスタンスのハンドルとに同じ結果となるよう適用されます。
 多重定義解決のための引数リストは左オペランド+演算リストの演算のリストです。 [注意:標準C++において、[] 内の構文項は演算であり、それは X[i,j] が配列記号がコンマ演算である(別の言葉で言えば、それは実際にはX[j]であり)正規の配列演算子であることを意味しています。 C++/CLI では、[] の中の最上位コンマはリスト切断子であり、演算子ではありません。そのため、X[i,j] は二つの引数を持つインデックス化プロパティとのみマッチします。 これはたとえ X がクラス型やクラスへのハンドルを持っていなくても真です。]
 CLI クラス型はデフォルト・インデックス化プロパティと operator[] の両方を持つべきではありません。 配列記号が文字列リテラルに適用された時、そのリテラルは "const char n 個の配列" か "const wchar_t n 個の配列" に適切に変換されます。 次のビルトイン演算子関数が存在します。
const char& operator[](<narrow-string-literal-type>, integer-type);
const wchar_t& operator[](<wide-string-literal-type>, integer-type);
const char& operator[](integer-type, <narrow-string-literal-type>);
const wchar_t& operator[](integer-type, <wide-string-literal-type>);
 ここで、integer-type は任意の整数型です。

15.3.2 関数呼び出し

 標準 C++(§5.2.2/1 )は明記します。
”関数呼び出しは、関数の引数として構成される、空も可能な、コンマ切りされた演算リストを含む括弧によって続けられた後置演算です。”
 C++/CLI はデリゲート(§27 )のサポートを含んでいます。その様な、後置演算はデリゲート型になり得ます。 その場合、全ての演算はデリゲート呼び出し(§27.3 )であり、そして、引数リストはデリゲートによってカプセル化された関数それぞれに引き渡されます。

15.3.3 明示的型変換(関数記述)

 ref クラスと値クラスの関数型キャストは変換の呼び出しをしません。 それらはただ、コンストラクタ呼び出しだけがあります。もし、対応するコンストラクタが存在しなければ、そのプログラムは不正です。[例:
value class C {};
value class E {
public:
    operator C() { return C(); }
};
void F(C c) {}
int main() {
    E e;
    F(C(e));  // エラー - C のコンストラクタにパラメータにマッチするものがありません
}


15.3.4 クラスメンバへのアクセス

 ハンドルで -> を協調して利用するために、標準C++(§5.2.5/2 )の文章は以下のように変更されます。
「第二オプション(矢印)のために、第一演算(ポインタ演算)の型は(完全型の)"クラス・オブジェクトへのハンドル"か、(完全型の)"クラス・オブジェクトへのポインタ"であるべきです。」
 標準C++(§5.2.5/3 )中の文章は以下のように訂正されます。
「もし、E1 が"クラス X へのポインタ"型を持つなら、その時、演算 E1 -> E2 は (*(E1)).E2 の形態と等価に変換されます。 もし、E1 が"クラス X へのハンドル"型を持ち、X が operator -> を持っていれば、演算 E1->E2 は (*(E1)).operator->(E2) と等価です。 そうでなければ、もし、E1 は"クラス X へのハンドル"型を持っており、X が operator-> を持っていない場合、その時、演算 E1->E2 は (*(E1)).E2 と等価の形に変換されます。
 脚注 59 は次のように拡張されます。
59) もし、E1 が "クラス X へのポインタ"型を持つ場合、その時、(*(E1)) は左辺値であることに注意してください。 もし、 E1 が"クラス X へのハンドル"型である場合、その時、(*(E1)) は gc 左辺値です。
 もし、値型のインスタンスに直接的に矢印演算子を使ってプログラムがアクセスしていれば、それは不正です。 [注意:値型のインスタンスへの矢印演算子を適用することはその値をボックス化しません。 しかしながら、その様なインスタンスへのドット演算子を利用した確かなアクセスはボックス化を必要とします。 メタデータの詳細は§34.5.1 を参照のこと。]
 文字列リテラルが二項演算子 operator-> への左向きオペランドであるとき、そのリテラルはSystem::String^に変換されます。

15.3.5 インクリメント、デクリメント

 §19.7.3 参照のこと。

15.3.6 動的型変換

 演算 dynamic_cast<T>(e) のために、標準C++(§5.2.7 )で規定されたルールに加えて、以下のものを適用します。
 もし、T が追跡参照型であれば、e は完全クラス型の gc 左辺値であり、その結果は T によって参照される型の gc 左辺値であるべきです。
 T はハンドル型を取ることができ、そして、その場合には、e は完全クラス型へのハンドルの右辺値になり、その結果は T 型の右辺値になるべきです。
 もし、e の値がヌル値であり、T がハンドル型であれば、その結果は T 型のヌル値です。
 もし、T が"cv1 B へのハンドル"であり、e が B が D の基底クラスであるような"cv2 D へのハンドル"型を持っていれば、その結果は e として同じ CLI ヒープ・ベース・オブジェクトを指し示す B へのハンドルです。 cv1 の cv 修飾子は cv2 の修飾子と同じかそれ以上であるべきです。そうでなければ、実行時チェックが必要となります。 もし、実行時チェックが成功しなければ、そのプログラムは不正です。
 もし、T がネイティブ・クラス以外の任意の型のハンドルかポインタであり、そして、キャストが失敗すれば、結果はヌル値か要請返却型となります。 もし、T がネイティブ・クラス以外の任意の型への参照で、キャストに失敗したら、その時、演算は System::InvalidCastException を投げます。T がネイティブ・クラスだった時、標準 C++ §5.2.7/9 のルールが適用されます。
 メタデータの詳細は、§34.5.2 を参照のこと。

15.3.7 型識別

 C++/CLI は typeid キーワードの新しい利用を、与えられた型名の System::Type^ を得るために、与えられた型名に ::typeid を続けることができるよう、追加します。 この生成物はここで(標準 C++ の typeid 演算とは無関係に) typeid Type expression (typeid 型演算) と参照します。 これを協調させるために、postfix-expression(後値演算)(§5.2 と§A.4 )のための標準 C++ 構文生成は拡張されます。(§15.3
 標準 C++(§14.6.2.2/4 )において、”以下の形式の演算”リストはpostfix-expression(後値演算)§15.3 )の新たな typeid 型演算形式を含むよう拡張されます。
 typeid 型演算の結果は静的型 System::Type^ の左辺値です。与えられた型にはただ一つの System::Type オブジェクトが存在します。 [注意:これは型 T について、T::typeid==T::typeid が常に真であることを意味しています。] この形態はコンパイル時演算なので、属性コンストラクタの引数として使われることができます。
 typeid 型演算での型名はロウ型(§12.3.1 )、ないし、ロウ型へのポインタであるべきです。
 typeid 型演算中の型は typedef を通して参照される型を提供する任意のハンドル R^ であり得ます。 その様な演算の結果は型 R に直接的に typeid をあてがったものと同じです。R% 型は同様の方法で扱われます。
 各々の基本型は別の型です。しかしながら、異なる基本型は同じ CLI 型にマップすることができます。 その様な、オプション、ないし、必須修飾子(§33.1 )がそれらの基本型を区別するために要請されているにもかかわらず、typeid 演算子は同じ CLI 型にマップするそれぞれの基本型は同じ Type ハンドルを生成するでしょう。 [例:int と long が共に System::Int32 にマップされている実装系において、int::typeid と long::typeid の双方は System::Int32 を記述する Type^ を結果とします。]
[注意:T 型の静的メンバを保護するために T::typeid にロックを使用する実践は、デッドロックを引き起こすことができるので、阻止されています。]
 typeid 型演算は System::Type::GetType() ライブラリ関数の機能にアクセスするための簡単な構文を提供します。 それは GetType() は与えられた型の CLI ヒープ・ベース・オブジェクト上で呼び出されるべきなので、::typeid は型に直接適用されることができ、従って、CLI ヒープ・ベース・オブジェクトを生成する必要はありません。[例:
using namespace System::Reflection;
ref class X { … };

Console::WriteLine(X::typeid);        // オブジェクトを必要としない
X^ pX = gcnew X;
Type^ pType = pX->GetType();       // GetType はオブジェクトが必要
Console::WriteLine(pType);

Console::WriteLine(Int32::typeid);
Console::WriteLine(array<Int32>::typeid);
Console::WriteLine(void::typeid);

Type^ t = String::typeid;
Console::WriteLine(t->BaseType);

array<MethodInfo^>^ functions = t->GetMethods();
for each (MethodInfo mi in functions)
    Console::WriteLine(mi);
 その出力です。
X
X
System.Int32
System.Single[]
System.Void
System.Object
…
System.CharEnumerator GetEnumerator()
System.Type GetType()


 ::typeid 演算子は型パラメータや生成型に適用されることが可能です。 その結果は型パラメータや生成型の実行時の型を表現する System::Type 型の CLI ヒープ・ベース・オブジェクトです。 ジェネリック型定義の本文の外側では、 ::typeid 演算子はその型のむき出しの名前に適用されるべきではありません。[例:
generic<typename T>
ref class X {
public:
    static void F() {
        Type^ t1 = T::typeid;            // オーケイ
        Type^ t2 = X<T>::typeid;   // オーケイ
        Type^ t3 = X::typeid;            // オーケイ
    }
};
int main() {
    Type^ t4 = int::typeid;              // オーケイ
    Type^ t5 = X<int>::typeid;     // オーケイ
    Type^ t6 = X::typeid;                // エラー
}
 明らかに、t6 の初期化はエラーです。しかしながら、t3 の初期化はそうではありません。 それは、X の使用は確かに X<T> の暗黙の利用になるからです。(§31.1.2 )]

 ::typeid 演算子は属性コンストラクタ呼び出しの引数に使われることができます。[例:
 [AttributeUsage(AttributeTargets::All)]
 public ref struct XAttribute : Attribute {
     XAttribute(Type^ t) { }
 };
 [X(int::typeid)]
 public ref class R {};

 標準C++ のネイティブ typeid は expression(演算)type-id(型識別子) に適用されることができます。 ネイティブ typeid は基本型、任意の列挙、ないし、ポインタ以外の任意の ref 型クラス、インターフェイス・クラス、ハンドル、値クラスの型に使われるべきではありません。 故に、これらの任意の型を持つ expression(演算)type-id(型識別子) にネイティブ typeid を含んでいる任意のプログラムは不正です。

15.3.8 静的型変換

 標準 C++(§5.2.9 )で規定されたルールを適用します。演算としては、static_cast<T>(e) には、以下もまた適用されます。
 静的型変換は標準 C++(§5.2.9/2 )で記述されているユーザー定義変換を含むことができます。以下の全てが考えられています。 明示的変換関数、暗黙的変換関数、明示的変換コンストラクタ、そして、暗黙的変換コンストラクタ。
[注意:非ネイティブ型は変換コンストラクタを持ちません。]
 標準C++(§5.2.9/3 )中で議論されたキャスト演算は追跡参照にもまた許されます。
 標準C++(§5.2.9/7 )中で議論された変換はネイティブと CLI の双方の列挙型にも許されます。
 "cv1 B へのハンドル"型の右辺値は、B がある型であるとして、もし、"D へのハンドル" から "B へのハンドル" への適切な変換が存在し(§14.2.1 )、 cv2 が同じか cv1 以上の cv-qualification(cv修飾子)であれば、D が B から派生したクラスである "cv2 D へのハンドル"型の右辺値に変換されることができます。 ヌル値は目的の型のヌル値に変換されます。

15.3.9 Reinterpret型変換

 標準 C++(§5.2.10 )で規定されたルールを適用します。reinterpret キャスト演算はハンドル型から、ないし、ハンドル型へのキャストの試みは不正です。
 reinterpret キャストはボックス化変換シーケンスを決して含むことはないでしょう。

15.3.10 Const型変換

 標準 C++(§5.2.11 )で規定されたルールを適用します。演算としては、const_cast<T>(v) は以下もまた適用します。
 標準 C++ はポインタへの const_cast の適用を議論しているので、そのルールはハンドルにもまた適用されるべきです。
 型 T1 の左辺値はもし、T1へのハンドルないしポインタが const_cast を使って T2 へのハンドルかポインタ型に明示的に変換することができるのであれば、キャスト const_cast<T2%> を使って明示的に型 T2 の左辺値に変換されることができます。 参照 const_cast の結果はオリジナルのオブジェクトを示します。
 ヌル値は目的の型のヌル値に変換されます。v に constキャスト演算として nullptr リテラルがあるプログラムは不正です。
 const キャストは決してボックス化変換シーケンスを含むべきではありません。

15.3.11 Safe型変換

 safeキャストはフレームワーク・プログラミングのための最適化キャストを実行します。コンパイラは safe_cast 演算を以下のように進めます。
  • コンパイラは名前 safe_cast を現在のコンテキスト中で検索を実行します。
  • もし、名前が曖昧さなく ::cli::safe_cast を参照するか、その名前が見つからなければ、その時、演算は次のような構文によってコンパイラに実行され、ここに規定されたルールによって解釈されます。
safe_cast < type-id > ( expression )
 演算 safe_cast<T>(v) の結果は型 T に演算 v を変換した結果です。 もし、T が追跡参照型であれば、その結果は gc左辺値で、そうでなければ、その結果は右辺値です。 型は safe_cast 中で定義されるべきではありません。safe_cast 演算子は const 性を外す型変換であるべきではありません。 型 T と v の型はネイティブ・クラス、ポインタ、メンバへのポインタ、ネイティブ参照、ないし、ネイティブ・クラスやポインタ、メンバへのポインタの間接的手段であるべきではありません。 [注意:言及した場合を除いて、目標の型や演算の型がどれかである safe_cast は常に検証可能です。 明示的型変換--Cスタイルキャストとして知られてもいます--は、引数が変換のための検証可能コードの生成を許す時、常に safeキャストの振る舞いをデフォルトとします。]
 演算 e は、もし、宣言"T t(e);" がいくつかの導入された一時変数 t について正形であれば、 safe_cast<T>(e) の形の safe_cast を使って型 T に明示的に変換されることができます。 その様な明示的変換の効果は、宣言や初期化を実行し、変換の結果として一時変数を使って実行したものと同じです。 もし、T が追跡参照型であれば、その結果は gc左辺値であり、そうでなければ右辺値です。演算 e はもし、そして、ただ初期化がそれを gc 左辺値として使う場合にのみ、gc左辺値として使われます。
 そうでなければ、safe_cast は以下の変換リストの一つを実行するべきです。明示的に safe_cast を使う以外の実行はあるべきではありません。
 任意の標準変換シーケンスの逆は、左辺値・右辺値、配列・ポインタ、関数・ポインタ、ポインタ変換、ポインタ・メンバ変換、そして、ブーリアン変換以外、safe_cast を使って明示的に実行することができます。 そのような safe_cast は明示的な変換は const 性を除去しない制限と、特定の場合において以下の追加ルールに従います。
  • 整数や列挙型の値は列挙型に明示的に変換されることができます。その値は、オリジナルの値が列挙値の範囲に含まれているのであれば、変わりません。 そうでなければ、結果となる列挙値は未規定です。
  • もし、T が"cv1 D へのハンドル"であり、v の型が"cv2 B へのハンドル"であれば、cv1 は cv2 と同じかそれ以上の cv qualification を持つべきであり、実行時チェックは D が B から継承されていることを決定するために適用されます。 (メタデータと結果の詳細は§34.5.1 参照のこと)。もし、変換に失敗した場合には、System::InvalidCastException が投げられます。 ハンドルの場合には、もし、v の値がヌル値であれば、その結果は型 T のヌル値です。 もし、実行時に変換が成功できなければ、そのプログラムは不正です。 [例:もし二つのrefクラス A と B が無関係で、b が B^ 型を持つ safe_cast<A^>(b) を使うプログラムでは、ダイナミック・チェックは成功できません。]
  • もし、T が"cv1 D への追跡参照"であり、v の型が"cv2 B"であれば、cv1 は cv2 と同じかそれ以上のcv qualification を持つべきであり、実行時チェックは D が B から継承されていることを決定するために適用されます。 (メタデータと結果の詳細は§34.5.1 参照のこと)。もし、変換に失敗した場合には、System::InvalidCastException が投げられます。 もし、変換が実行時に成功できなければ、そのプログラムは不正です。
  • 型 "cv1 R へのハンドル" の右辺値は、値型である 型 V の左辺値に変換されることはできません。 R は System::Object、System::ValueType、ないし、V を実装するインターフェイスであるべきです。 もし、V が列挙型であれば、R はまた System::Enum であることができます。(メタデータと結果の詳細は§34.5.1 を参照のこと)。 もし変換が失敗したら、System::InvalidCastException が投げられます。この変換シーケンスはunboxing(非ボックス化)と呼ばれます。 [注意:safe_cast は非ボックス化を結果とすることができる唯一の型変換です。]

15.4 単項表記


15.4.1 単項演算子


15.4.1.1 単項 &
 型 T の左辺値に適用された時、& は T* をもたらします(標準 C++ §5.3.1/2 参照)。 型 T の gc左辺値に適用された時、& は interior_ptr<T> をもたらします(§12.3.6 )。
 ref クラス型のインスタンス、リテラル・フィールド、ないしプロパティやクラス・コンストラクタの外側で initonly フィールドにビルト・イン単項演算子 & を適用しようと試みるプログラムは不正です。
 デリゲートの生成中以外で、任意のコンテキストで非ネイティブ・クラスのメンバ関数のアドレスを取得しようと試みるプログラムは不正です。非ネイティブ・クラスのメンバのメンバへのポインタ表現はありません。[例:
delegate void D(int i);
ref struct R {
    static  void M1(int a) { }
            void M2(int b) { }
    virtual void M3(int c) { }
};
int main() {
    R^ r = gcnew R;
    D^ d;
    d =  gcnew D(&R::M1);
    d =  gcnew D(r, &R::M2);
    d += gcnew D(r, &R::M3);
}

 デリゲート生成のためのメタデータの詳細は§34.14 を参照のこと。

15.4.1.2 単項 *
 標準C++(§5.3.1/1 )はハンドルでの間接的指定を許すために拡張されています。特に、次の文、
 単項演算子 * は indirection (間接的指定)を実行します。:それが適用された演算はオブジェクト型へのポインタか、関数型へのポインタであるべきであり、その結果は演算が示す関数やオブジェクトを参照する左辺値です。もし、演算の型が "T へのポインタ" であれば、その結果の型は "T" です。
はこれらで置き換えられます。
 単項演算子 * は indirection (間接的指定)を実行します。:それが適用された演算は次の一つであるべきです。:
 
  • もし、演算がオブジェクト型へのポインタか関数型へのポインタであれば、その時、その結果は演算が示す関数やオブジェクトを参照する左辺値です。 もし、演算の型が "T へのポインタ" であれば、その結果の型は "T" です。
  • もし、演算がオブジェクト型へのハンドルであれば、その時、その結果は演算が示すオブジェクトを参照する gc 左辺値です。もし、演算の型が "T へのハンドル" であれば、その結果の型は "T" です。
 参照外し T^ は型 T の gc 左辺値をもたらします。
 operator* が文字列リテラルに適用された時、そのリテラルは各々、" const char n 個の配列" か、" const wchar_t n 個の配列" に変換されます。 次のようなビルト・イン関数が存在します。:
const char& operator*(<narrow-string-literal-type>);
const wchar_t& operator*(<wide-string-literal-type>);
[注意:ユーザー定義演算子はハンドルと働くことができるので、ref 型、値クラスはインスタンス単項演算子 * を持つ時、その様なクラスのハンドルの参照外しは実際にハンドルの参照外しよりもむしろユーザー定義演算子を呼び出すでしょう。 これはなぜなら、全てのインスタンス演算子はクラス型同様にそのクラスのハンドルにも機能するからです(標準 C++ §19.7.1 )。例えば、
ref struct R {
    int operator*() {
        Console::WriteLine("R::operator*");
        return 42;
    }
};
int main() {
    R^ r1a = gcnew R;
    int x = *r1a;    // operator*() を呼ぶ
    R r1b;
    x = *r1b;        // operator*() を呼ぶ
}
 これはプログラマにとって驚くべきことかもしれませんが、高品質な実装では、ref クラスや値クラスがインスタンス operator* を持つ時、警告するべきです。 その様な演算子の用意された代換えは静的演算子のペアなので、そのオペランドは次のように明らかにクラス型かクラス型のハンドルのどちらかに位置しています。
ref struct R {
    static int operator*(R^ r) {
        Console::WriteLine("R::operator*(R^)");
        return 42;
    }
    static int operator*(R% r) {
        Console::WriteLine("R::operator*(R%)");
        return 42;
    }
};
int main() {
    R2^ r2a = gcnew R2;
    int x = *r2a;    // operator*(R^) を呼ぶ

    R2 r2b;
    x = *r2b;        // operator*(R%) を呼ぶ
}


15.4.1.3 単項 %
 単項演算子 % の結果はそのオペランドへのハンドルであり、そのオペランドは、通常、gc 左辺値です。 しかしながら、オペランドが値クラスのインスタンスであれば、その結果は右辺値であり、型は "T へのハンドル" です。 特に、"cv T" 型のオブジェクトのハンドルを取得することは、同じ cv-qualifier である "cv T へのハンドル" です。 もし、T が値クラスであれば、演算は(cv-qualification のロスを許す)ボックス化変換シーケンスを呼び出し、その結果は右辺値です。[例:
ref class R {};
value class V {};
void f(System::Object^ o) {}
void g() {
    R r;
    f(%r);
    V v;
    f(%v);     // v はボックス化される
}

[注意:同じ CLI ヒープ・ベース・オブジェクトへの全てのハンドルは同等に比較します。値クラスには、% はボックス化操作であるので、% の複数のアプリケーションはハンドルを同等ではないという結果となります。]
 ネイティブ・クラス型へ単項 % 演算子を適用するプログラムは不正です。

15.4.1.4 単項 ^
 その様な演算子は存在しません。[根拠:結果として、%/^ と &/* との間に非対称性があり、それは、単項 * は * と ^ の双方の参照外しに使われるためです。 しかしながら、後のケースで使われる単純な構文を許すために、不可知のテンプレートやジェネリクスの記述を許します。 任意のイベントにおいて、この演算子を追加することはなんの新しい構文ももたらしたりしません。そして、新しい構文によってその様な演算子が後付で追加されることを妨げるでしょう。]

15.4.1.5 論理否定
 標準C++ (§5.3.1/8 )は次のように変更されます。:
 論理否定演算子 ! のオペランドは暗黙のうちに bool に変換されます(第4節)。 その値は、もし、変換されたオペランドが false であれば true、そうでなければ、false です。 もし、bool への暗黙の変換が不正であり、オペランドはハンドル型か値クラス拘束によって拘束されていないジェネリック型パラメータによって与えられた型であれば、 その値は、もし、ハンドルがヌルであれば true で、ハンドルがヌルでなければ false です。結果の型は bool です。[例:
ref class R { ... };
R^ r = ...;

if (!r)
    // ハンドルはnull値
else
    // ハンドルは非null値


15.4.2 インクリメント、デクリメント

 §19.7.3 を参照のこと。

15.4.3 sizeof

 標準C++ (§5.3.3/1 )は次のように拡張されます。
sizeof 演算子は、関数や不完全型を持つ演算、その列挙の全てが宣言される前の列挙型、その様な型の括弧付きの名前、ビット・フィールドを指定する左辺値、ヌル型を持つ演算、ハンドル、追跡参照、ないし、re クラス、に適用されるべきではありません。 sizeof(char), sizeof(signed char), そして、sizeof(unsigned char) は 1 です。;sizeof を任意の他の基本型(3.9.1 )に適用した結果は実装定義です。 [注意:特に、sizeof(bool)==と==、sizeof(wchar_t)、sizeof(short int), sizeof(int), sizeof(long int), sizeof(long long int), sizeof(float), sizeof(double), そして、sizeof(long double) は実装定義です。]
 次のパラグラフが標準C++(§5.3.3/2 )の後ろに挿入します。
値クラス型やジェネリック型パラメータに適用された時、その結果はコンパイル時定数演算ではありません。 [注意:値クラス型の定義は基本型とポインタを排除するので、基本型とポインタの sizeof 演算は依然、コンパイル時定数演算です。]
ref クラスやインターフェイス型に適用された時、そのプログラムは不正です。
 CLI 標準によって課せられた勧告に従うために、size_t は最低でも 4 バイトの符号無し整数であるべきです。

15.4.4 new

 単純値クラス(§22.4 )以外の CLI クラス型のオブジェクトを new を使ってメモリの領域確保を試みるようなプログラムは不正です。

15.4.5 delete

 標準C++(§5.3.5/1 )は CLI ヒープ上に領域確保されたオブジェクトの削除を許すために、次のように拡張されています。
オペランドはポインタ型、ハンドル型、ないし、ポインタへの単一変換関数(12.3.2 )を持っているクラス型を持つべきです。
最初の代換え案( delete object )では、 delete のオペランドの値は非配列オブジェクトへのポインタやハンドルか、その様なオブジェクト(第10節)の基底クラスを表すサブ・オブジェクト(1.8 )へのポインタであるべきです。 もしそうでなければ、その振る舞いは不定です。
もし、delete 演算が領域削除関数(3.7.3.2 )の実装を呼び、そして、もし、delete 演算のオペランドはヌル・ポインタ定数でなければ、領域削除関数はポインタやハンドルが無効を示すように、ポインタやハンドルに参照される格納領域を領域削除するでしょう。

 delete の配列形式はハンドル型に使われるべきではありません。
 ジェネリックの内側では、もし、オブジェクトの型がジェネリック型パラメータであれば、delete はそのオブジェクトのデストラクタを呼び出すために使われることができます。 もし、ジェネリック型パラメータが System::IDisposable インターフェイスに拘束されていれば、delete 演算はそのオブジェクトのインターフェイスを通して呼び出しを評価します。 もし、ジェネリック型パラメータが System::IDisposable インターフェイスに拘束されていなければ、オブジェクトは System::IDisposable^ にダイナミック・キャストを使って型変換され、もしハンドルがヌルでなければ変換されたオブジェクトを通して呼び出しが実行されます。 [注意:後者の場合、変換はジェネリック型パラメータが値型になることができる場合、ボックス化を要請するかもしれません。 無視してよいほどのボックス化変換のパフォーマンス・オーバーヘッドと IDisposable^ へのダイナミック・キャストを安全にするため以外に、ボックス化オブジェクトのデストラクタ呼び出しは、値型のデストラクタは何もなさないが故に(それらはユーザーは定義できないので)、プログラム上、なんの構文的衝突を起こさないでしょう。]

15.4.6 gcnew 演算子

 gcnew 演算子はその形式が CLI ヒープ上にオブジェクトを生成するという点を除いて、new 演算子に似ています。gcnew 演算子の結果の型は領域確保されたオブジェクトの型へのハンドルです。メモリ不足の状況では、gcnew は System::OutOfMemoryException を投げます。
 gcnew には配列形式はありません。gcnew の配置形式はありません。gcnew 演算子は多重定義したり置き換えることはできません。gcnew のクラス特殊化はありません。
 gcnew を使ってネイティブ・クラス型のオブジェクトのためにメモリを領域確保しようとするプログラムは不正です。
 標準 C++ (§5.3.4 )では、new-expression (new 演算) は実行時にオブジェクトのメモリを領域確保するために使われます。この文法は gcnew 演算子を協調的に追加するために、次のように拡張されています。
new-expression:
  ::opt new new-placementopt new-type-id new-initializeropt
  ::opt new new-placementopt ( type-id ) new-initializeropt
  gcnew type-specifier-seq new-initializeropt array-initopt
 gcnew の場合には、領域確保されたオブジェクトの型は抽象クラス型であるべきでも不完全型でもありません。 array-init (配列初期化)は CLI 配列(§24.2 参照)を生成する時にのみ使われるべきです。 [注意:値クラスに適用された gcnew 演算子はボックス化値を生成します。]
 gcnew 演算子はデリゲートのインスタンスを生成するために使われます。より詳しい情報は、§27.2 を参照のこと。

15.4.7 throw 演算

 throw-expression (throw 演算)からハンドラに制御が渡されると、finally-clause (ファイナリ節)が、もしあれば、処理した全ての try-block ないし、function-try-block について、ハンドラを含む try-blockfunction-try-block を処理したので、呼び出されます。 finally-clause(ファイナリ節) はその親となる try-blockfunction-try-block の呼び出しの逆順に呼び出されます。
 任意の与えられた try-blockfunction-try-block 中のオブジェクトの自動削除が、標準 C++(15.2 )で、その try-blockfunction-try-block に関連づけされた任意の finally-clause(ファイナリ節) の呼び出しに先立って起きることが要請されています。
 例は§16.4 参照のこと。
 もし、オブジェクトがハンドルによって投げられた場合(そのハンドルが参照するクラスの種類にかかわらず)、使われる例外処理メカニズムは CLI によって定義されたものであるべきです。(これについては、ボックス化値型も含みます。)そうでなければ、標準 C++ メカニズムが使われるべきです。
 ほとんど全ての型のオブジェクトを投げることができます。このルールの例外は、値や参照で ref クラスと値クラスが投げられるものです。ハンドルによってオブジェクトを投げることは常に許されています。この標準に準拠した以外での、CLI メカニズムを使って投げられるべきでない型のセットは標準 C++ のものと同様です。
 nullptr を投げようと試みるプログラムは不正です。

15.5 明示的型変換(cast 表記)

 標準C++(§5.4/5 )中のルールは C++/CLI のために静的キャストの前に safe キャストを含めるために拡張されています。
  • const_cast
  • safe_cast
  • const_cast が続く safe_cast
  • static_cast
  • const_cast が続く static_cast
  • reinterpret_cast
  • const_cast が続く reinterpret_cast
[注意:標準 C++ はこれによって、safe キャストは演算型や標的型がネイティブ・クラスであれば不正となるため、変更することなく残ります。]
 もし、引数の型とそれに変換された型の双方がネイティブ・クラス、ポインタ、メンバへのポインタ、ネイティブ参照、ないし、ネイティブ・クラス、ポインタ、メンバへのポインタへの非直接的指示でなければ、その時、明示的型変換は static_cast や reinterpret_cast を使うべきではありません。 [注意:引数が CLI クラス型を含む時、明示的型変換は常に検証可能な結果を生成します。これはプログラマに他の言語のキャスト記法のためのもっとも適切な代換え物として明示的型変換構文を使うことを可能とします。]

15.6 加算演算子


15.6.1 デリゲート型結合

 全てのデリゲート型は、D がデリゲート型であるとして、次の定義済み演算子を提供します。
 static D^ operator +(D^ x, D^ y);
 二項 + 演算子はオペランドの双方が同じデリゲート型 D のインスタンスである時デリゲート結合を実行します。その演算子の結果は System::Delegate::Combine(x,y) の呼び出しの結果であり、D^ に結果をキャストします。 [注意:デリゲート結合の例としては、§15.6.1 §27.1 を参照のこと。System::Delegate はそれ自身はデリゲート型ではないので、operator+ はそれ用には定義されていません。そのオペランドのどちらかが nullptr である時の振る舞いは、§27.1 で記述されています。]

15.6.2 デリゲート型削除

 全てのデリゲート型は、D がデリゲート型であるとして、次の定義済み演算子を提供します。
 static D^ operator -(D^ x, D^ y);
 二項 - 演算子は双方のオペランドが同じデリゲート型 D のインスタンスである時デリゲート削除を実行します。その演算子の結果は System::Delegate::Remove(x,y) の呼び出しの結果であり、D^ に結果をキャストします。
[注意:+= と -= 演算子は代入演算子合成(§19.7.4 )を通して定義されています。オペランド y が nullptr である時の振る舞いは、§27.1 に記述されています。]
[例:
 delegate void D(int x);
 ref struct Test {
     static void M1(int i) { ... }
     static void M2(int i) { ... }
 };
 int main() {
     D^ cd1 = gcnew D(&Test::M1);
     D^ cd2 = gcnew D(&Test::M2);
 
     D^ cd3 = cd1 + cd2;
     cd3 -= cd1;
     cd3 += cd1;
     cd3 = cd3 - (cd1 + cd2);
 }


15.6.3 文字列連結

 二項演算子 + が文字列リテラルに適用された時、その文字列はSystem::String^ に変換されます。 結果として、任意の整数型を持つ値が文字列リテラルに追加された時、文字列結合する結果となります。 [注意:標準C++ からの挙動でこの変更は故意になされたものです。]
 次のようなビルト・イン演算子関数が存在します。:
 System::String^ operator+(<narrow-string-literal-type>, integer-type);
 System::String^ operator+(<wide-string-literal-type>, integer-type);
 System::String^ operator+(integer-type, <narrow-string-literal-type>);
 System::String^ operator+(integer-type, <wide-string-literal-type>);
 integer-type (整数型) は任意の整数型です。二項 + 演算子のオペランドの一つが System::String^ である時、文字列結合を結果とします。 もし、他のオペランドも System::String^ 型を持っていなかった場合には、その値はその型に ToString 関数を呼び出すことによって変換されます。 次のようなビルト・イン演算子関数が存在します。:
 System::String^ operator+(System::String^, System::String^);
 System::String^ operator+(System::String^, System::Object^);
 System::String^ operator+(System::Object^, System::String^);
[例:
 Point^ p = gcnew Point(5,6);
 String^ s = "C++" + L"/CLI";  // s => "C++/CLI"
 s = 3 + " apples";            // s => "3 apples";
 s = "p is " + p;              // s => "p is (5,6)"
]  これら三つのビルト・イン関数はユーザー定義版で隠蔽されることが可能です。[例:プログラム
 String^ operator+(String^ l, String^ r) { return l; }
 
 int main() {
     System::Console::WriteLine("ABC" + "DEF");
 }
は "ABC" を印字します。]
 strlit が文字列リテラルであり、intexp が任意の整数型演算である場合において、strlit - intexp 形式の演算を含むプログラムは不正です。

15.7 シフト(Shift)演算子

 型 long long int と型 unsigned long long int を協調的に追加するために、標準C++(§5.8/2 )は次のように変更されます。
E1 << E2 の値は E1 (をビットパターンが記述されているものとして)を E2 ビット位置だけ左にシフトされたものです。 空いたビットは 0 埋めされます。もし、E1 が符号無し型であれば、その結果の値は 2 の E2 累乗が E1 に掛け合わされたものであり、もし、E1 が unsigned long long int 型であれば、ULLONG_MAX + 1 、もし、E1 が unsigned long 型であれば ULONG_MAX + 1 、さもなければ UINT_MAX + 1 だけ剰余を切りつめたものです。 [注意:定数 ULLONG_MAX、 ULONG_MAX、そして、UINT_MAX はヘッダ <climits> で定義されています。]

15.8 関係演算子

15.8.1 ハンドル等価演算子

 全ての ref クラス型と値クラス型 C は暗黙のうちに次の定義済み等価演算子を提供します。
bool operator ==(C^ x, C^ y);
bool operator !=(C^ x, C^ y);
 暗黙のうちに提供されるハンドル等価演算子は、もし多重定義解決が適切な等価演算子(ユーザー定義かそれ以外のこの仕様上で定義されたもの)を見つけられなかった時にのみ使われます。[例:デリゲートと System::String は定義された等価演算子をすでに持っています。もし、多重定義解決がそれらの演算子の一つを選択した場合、暗黙のうちに定義されたハンドル等価演算子は適用されません。]
 ハンドル等価演算子が適切な時を決定するための特殊なルールがあります。オペランドが型 A^ と B^ である equality-expression (等価演算) について、A0 が次のように定義します。
  • もし、A が ref クラスとして知られているジェネリック型パラメータである場合、A0 を A の実効基底クラスとする。
  • そうでなければ、もし、A がインターフェイス型、ref クラス型、ポインタ以外の値型、ないし、ヌル型である場合、A0 を A と同じであるとする。
  • そうでなければ、適切な暗黙のハンドル等価演算子はない。
 今、A1 を次のように定義します。
  • もし、A0 がインターフェイス型、デリゲート型、System::Delegate、ないし、System::String である場合、A1 を System::Object とする。
  • そうでなければ、もし、A0 が CLI 配列型である場合、A1 を System::Array とする。
  • そうでなければ、A0 がヌル型、ref クラス型、ないし、ポインタ以外の値型であり、A1 は A0 と同じとする。
 B0 と B1 を同じ振る舞いで定義します。今、任意の暗黙のハンドル等価演算子が適切であるかどうか次のように決定します。
  • もし、型 A と型 B の双方がヌル型であった場合、その時、多重定義解決は実行されず、その結果は operator== については常に true であり、operator!= については false です。
  • そうでなければ、もし、なんの A0^ から B0^ への識別変換もハンドル変換も、B^ から A^ への識別変換もハンドル変換もない場合、その時、なんの適切な暗黙のハンドル等価演算子もありません。
  • そうでなければ、もし、A1^ から B1^ への識別変換ないし、ハンドル変換がある場合、その時、B1 のための暗黙のハンドル演算子が適切となります。
  • そうでなく、B1^ から A1^ へのハンドル変換がある場合、その時、A1 のための暗黙のハンドル演算子が適切となります。
  • そうでなければ、適切なハンドル等価演算子はなにもありません。
 もし、equality-expression (等価演算) のオペランドがハンドルでなければ、適切なハンドル等価演算子はなにもありません。
[注意:このルールは次の言外の意味を持っています。
  • 暗黙のハンドル等価演算子は違うとわかっている型を比較するために使うことはできません。例えば、System::Object から派生した二つの型 A と B は決して識別のために比較することに成功することはあり得ません。同様に、A が ref クラスであり、B が A の実装していないインターフェイスである場合、その時、なんの暗黙のハンドル等価演算子も適用されません。
  • 暗黙のハンドル等価演算子はユーザー定義等価演算子を除いて値クラスのオペランドの比較を許しません。
  • 暗黙のハンドル等価演算子は決してオペランドに起きるボックス化変換を引き起こすことはありません。その様な変換は意味がありません。

 多重定義解決ルールは暗黙のハンドル等価演算以外の等価演算を選択した時、暗黙のハンドル等価演算の選択はオペランドの一つ、ないしは双方を System::Object^ に明示的にキャストすることで強制することができます。

15.8.2 デリゲート型等価演算子

 全てのデリゲート型は暗黙のうちに次のような定義済み比較演算子を提供します。
 bool operator ==(Delegate^ x, Delegate^ y);
 bool operator !=(Delegate^ x, Delegate^ y);
 これらは System::Delegate::Equals の項で実装されています。もし、二つのオペランドが違うデリゲート型なら、その演算は不正です。[根拠:二つの異なるデリゲート型は決して等価性の結果に成功したりしません。多重定義解決は実行時に等価性の失敗を引き延ばすために双方のデリゲート型を System::Delegate に促すことができます。]

15.8.3 文字列等価

 System::String ハンドルの等価性は System::String::operator== と System::String::operator!= で定義されています。

15.9 論理積演算子

 標準C++ (§5.14/1 )は次のように変更されています。
&& 演算子は左右をグループ化します。そのオペランドは双方とも暗黙のうちに bool 型(第4節)に変換されます。 もし、その変換が不正であり、オペランドがハンドル型か値クラス拘束によって拘束されていないジェネリック型パラメータによって与えられた型であれば、オペランドはヌル値とテストされ、もし、ヌルでなければ true をヌルであれば false を返します。そうでなければ、もし、bool への変換が不正であり、オペランドはハンドル型や値クラス拘束で拘束されたジェネリック型パラメータで与えられた型でなければ、プログラムは不正です。 その結果は、もし、オペランドの双方が true であれば true、そうでなければ false です。& と違って、&& は左右の等価性を保証します。2番目のオペランドは、もし、最初のオペランドが false であれば、評価しません。

15.10 論理和演算子

 標準C++ (§5.15/1 )は次のように変更されます。
|| 演算子は左右をグループ化します。そのオペランドは双方とも暗黙のうちに bool 型(第4節)に変換されます。 もし、その変換が不正であり、オペランドがハンドル型か値クラス拘束によって拘束されていないジェネリック型パラメータによって与えられた型であれば、オペランドはヌル値とテストされ、もし、ヌルでなければ true をヌルであれば false を返します。 そうでなければ、もし、bool への変換が不正であり、オペランドはハンドル型や値クラス拘束で拘束されたジェネリック型パラメータで与えられた型でなければ、プログラムは不正です。 その演算は、もし、そのオペランドのどちらかが true であれば、true を、そうでなければ、false を返します。| と違って、|| は左右の等価性を保証します。さらに、もし、最初のオペランドが true に評価された場合には、2番目のオペランドは評価されません。

15.11 条件演算子

 次の形態の演算について、
e ? p : nullptr
e ? nullptr : p
e ? h : nullptr
e ? nullptr : h
 e は bool に暗黙のうちに変換されることができる演算であり、p はポインタ型で、h はハンドル型を持っており、標準C++(§5.16/6 )は変更されます。
二番目と3番目のオペランドはポインタ型持ち、もしくは、一つがポインタ型を持ち、そして、他方がヌル・ポインタ定数、ないし、ヌル値定数です。ポインタ変換と資格変換はそれらを複合ポインタ型にして実行されます。その結果は複合ポインタ型のものです。もし、二番目か3番目のオペランドのどちらかがハンドル型を持っており、他方のオペランドがヌル値定数であれば、その結果はそのハンドル型のものです。

15.12 代入演算子

 演算 E1 op = E2 において、合成の後、演算はE1 = E1 op E2 として扱われるので、E1 はプロパティであり得ます。
 E1 がプロパティである、E1 = E2 の形態の代入演算の結果を使おうと試みるプログラムは不正です。[注意:その様な演算の結果の型は E1 の型であり、プロパティの set アクセサ関数は void 型を持っているので、その結果は void 型を持っています。]
 複合代入演算子の合成についての情報は(§19.7.4 )を参照してください。プロパティとイベントの再書き込みルールは§15.14 でカバーされています。
 代入の左オペランドは左辺値、ないし、gc左辺値であるべきです。

15.13 定数演算子

 標準 C++(§5.19/2 )は「非ローカル静的オブジェクトの初期化の目的でのみ constant-expressions (定数演算) であると考えられている他の演算」のリストを提供します。そのリストは次の追加によって拡張されています。
  • ヌル値定数
 リテラル・フィールドは任意のコンテキストで同じ型のリテラルを許すために使われることができます。その様な、リテラル・フィールドはコンパイル時定数演算に存在することができます。
 リテラル・フィールドを協調的に追加するために、次のものが標準 C++ の§5.19/3 の後に挿入されます。
リテラル定数演算は算術的定数演算、System::String^ 型の文字列リテラル、そして、ヌル値定数 nullptr を含んでいます。
 リテラル値にのみ使う文字列結合演算はコンパイラによって評価されることができ、その結果、コンパイル時演算であると考えられています。[例:
#define X 42

ref struct R {
    literal String^ Truth = "The meaning of life is " + X;
};

 静的 const 変数が #using を通してスコープ中にもたらされた時、コンパイラはそれをリテラル値として扱うことができません。故に、それは(テンプレート非型引数やネイティブ配列サイズのような)リテラルが必要な時のコンテキスト中で使われることができません。しかしながら、静的 const 変数が #include を通してもたらされた時には、それがリテラルとして使われるかどうかについては標準 C++ のルールに従います。

15.14 プロパティとイベント書き換えルール

 検索のために、プロパティはクラス・データ・メンバとして扱われます。一つ以上のプロパティを含む演算の評価はこれらのプロパティにアクセサ関数(§19.5.3 )を使って書き換えされる演算を要請します。
 プロパティ演算がアクセサ関数を使って書き換えられる前に、演算子合成ルール(§19.7.4 )がその演算に適用されるべきです。(結果として、プロパティ書き換えプロセスは決して複合代入演算に出会うことはないでしょう。)
 @ が二項演算子を表すものとして、演算 E1 @ E2 について考えます。もし、E2 がプロパティであれば、それはさらなる評価の前にプロパティの get アクセサ関数呼び出しとして書き換えられるべきです。もし、E1 がプロパティであれば、その時、もし、@ が単純代入演算子であれば、演算はプロパティの set アクセサ関数呼び出しとして書き換えられるべきです。そうでなければ、E1 はプロパティの get アクセサ関数呼び出しとして書き換えられるべきです。
 もし、演算 E がプロパティを評価し、E は二項演算子のオペランドでなかった場合、E はプロパティの get アクセサ関数呼び出しとして書き換えられるべきです。
 プロパティ演算の書き換えはスカラーとインデックス化プロパティとで異なっています。もし、P がスカラ・プロパティ(§19.5 )である場合、
  • プロパティ get 書き換えは P::get() であるべきです。
  • プロパティ set 書き換えは P::set(expression) であり、expression は単純代入演算子演算の右手側に対応しています。
 もし、E がインデックス化プロパティ(§19.5 )である場合、それは一般に P[expression-list] の形式を持ちます。
  • プロパティ get 書き換えは P::get(expression-list) であるべきです。
  • プロパティ set 書き換えは P::set(expression-list, expression) であり、expression は単純代入演算子演算の右手側に対応しています。
[例:与えられた P, Q、そして、R がスカラ・プロパティとして、演算
P += Q * !R
は演算子合成によって変換され、
P = P + Q * !R
それから、書き換えられます。
P::set(P::get() + Q::get() * !R::get())
 加えて、与えられた A, B、そして、C がインデックス化プロパティだとして、演算
A[i] = B[j, k] + C[l, m, n]
は書き換えられます。
A::set(i, B::get(j,k) + C::get(l, m, n))

 前置と後置の ++ と -- 演算子の置き換えルールは§19.7.3 で議論されています。
 もし、検索がクラス中に複数の同じ名前のプロパティを見つけた場合、P[expression-list] の形式の演算は常に(例え、付記数の数が任意の存在するプロパティにマッチしなくても)インデックス化プロパティ・アクセスとして解釈されるべきです。もし、ただ一つのプロパティがスカラ・プロパティであった場合には、その使われる書き換えルールはスカラ・プロパティ get であるべきであり、配列要素演算子はそのプロパティ get の結果が適用されるべきでしょう。
[例:次の例において、クラス R は P という名前のただ一つのプロパティを持っています。それはスカラ・プロパティであるため、配列要素演算子はそのプロパティの結果に適用されます。
 ref struct R {
     property String^ P { String^ get() { ... } }
 };
 int main() {
     R^ r = gcnew R;
     wchar_t c = r->P[0};    // 文字列のデフォルト・インデックス化プロパティを呼びます
 }
 次の例では、R は X という名前の二つのプロパティを持っています。故に、X への全ての配列要素はインデックス化プロパティと解釈されます。書き換えの多重定義解決にマッチする set 関数が存在しないので、次のようなコードは不正です。
 ref class R {
     array<int>^ MyArray;
 public:
     R() { MyArray = gcnew array<int>(10); }
 
     property array<int>^ X {
         array<int>^ get() { return MyArray; }
     }
 
     proeprty int X[int] {
         int get(int i) { return i*i; }
     }
 };
 
 int main() {
     R r;
     r.X[2] = 1;      // エラー - R::X::set(int,int) は存在しません
     int y = r.X[2];  // R::X::get(int) を呼びます
 }

 プロパティ演算が書き換えられた後、結果となる演算は存在するルールを使って再評価されます。その時、多重適宜解決が受け入れ可能な関数を見つけることに失敗するかもしれないことが起こりえ、その場合、そのプログラムは不正です。[例:インデックス化プロパティはまだ必要な数の引数を取るプロパティ・アクセス・メソッドを置き換えられません。 もし、プロパティがただ get アクセサ関数を持っているだけであれば、未だ、そのプロパティを含む演算はプロパティ set に置き換えられ、検索は set アクセサ関数を見つけることに失敗するでしょう。]
 書き換えられる前は、プロパティはフィールドのように働きます。その様な、検索がプロパティ名称を見つけた時、クラスが hidebysig クラス(§10.7 )であったとしても、それ以上プロパティ名称を基底クラス中に見に行くことはありません。しかしながら、書き換えられた後は、プロパティのアクセサ関数は他の関数を hidebysig 検索するのと同じルールに従います。
 複合代入演算子の左オペランドがイベントである時、演算子合成は適用されるべきではありません。
 与えられた演算 E1 @ E2 は、@ が二項演算子を表現しており、もし、E1 がイベントであれば、イベントは次のようなルールで書き換えられます。
  • もし、@ が += であれば、演算はイベント追加として、E1::add(E2) に置き換えられる。
  • もし、@ が -= であれば、演算はイベント削除として、E1::remove(E2) に置き換えられる。
 そうでなければ、プログラムは不正です。
 与えられた演算 E(expression-list) について、もし、E がイベントであれば、演算はイベント起動として E::raise(expression-list) に書き換えられます。
 演算中でのイベントのそれ以外の全ての使い方は不正です。
[例:V はイベントであり、D がデリゲートとして、演算 V += D は V::add(D) として置き換えられ、演算 V -= D は V::remove(D) として置き換えられ、演算 V(this, e) は V::raise(this, e) として置き換えられます。]
 イベント演算が書き換えられた後、それは存在するルールを使って再評価されます。その時、多重定義解決が受け入れ可能な関数の検索に失敗することは可能で、その場合、プログラムは不正です。
[例:デリゲートはもしそれらが異なったデリゲート型を持っている場合イベントに追加することはできません。]

文責:翻訳 => ヽ(゚∀。)ノうぇね