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

19.1 クラス定義

 標準 C++ (§9)において、class-specifier(クラス指定子)はクラスを定義するために使用されます。 この文法は、次のように、public クラスや private クラスの追加を協調させるよう拡張されています。
class-specifier :
  attributesopt top-level-type-visibilityopt class-head { member-specificationopt }
 attribute(属性)§29 に、top-level-visibility(上層型認識)については §12.4 に記述されています。
 class-head(クラス先頭)(§9 )はクラス修飾子(§19.1.1 )をサポートするために拡張されています。
class-head :
  class-key identifieropt class-modifiersopt base-clauseopt
  class-key nasted-name-specifier identifier class-modifiersopt base-clauseopt
  class-key nasted-name-specifieropt template-id class-modifiersopt base-clauseopt
 class-key(クラスキー)(§9 )は ref クラス(§21 )、値クラス(§22 )、そして、インターフェイス・クラス(§25 )をサポートするために拡張されています。
class-key :
  class
  struct
  union
  ref#class
  ref#struct
  value#class
  value#struct
  interface#class
  interface#struct
 initonlyフィールド、リテラル・フィールド、デリゲート、イベントなどの追加を協調させるよう標準 C++ (§9.2 )にあるクラスの member-declaration(メンバ宣言)の構文は次のように拡張されます。
member-declaration :
  attributesopt initonly-or-literalopt decl-specifier-seqopt member-declarator-listopt ;
  function-definition ;opt
  ::opt nested-name-specifier templateopt unqualified-id ;
  using-declaration
  template-declaration
  generic-declaration
  delegate-specifier
  event-definition
  property-definition

initonly-or-literal :
  initonly
  literal
 属性は§29 に、initonlyフィールドは§19.12 に、リテラル・フィールドは§19.11 に、ジェネリクスは§31 、デリゲートは§27 に、イベントは §19.6 に、そして、プロパティは §19.5 に記述されています。
 メタデータの詳細は、§34.7.1 を参照のこと。

19.1.1 クラス修飾子

 sealed と abstract クラスの追加を協調的に行うために、標準 C++ (§9 )の class-head(クラス先頭) の構文はクラス修飾子のオプション的シーケンスに含むために次のように拡張されています。
class-modifiers :
  class-modifiersopt class-modifier

class-modifier :
  abstract
  sealed
 もし、class-modifiers (クラス修飾子群)に複数回同じ修飾子が現れるなら、そのプログラムは不正です。
[注意:abstract と sealed は一緒に使うことができます。それらが互いに排他的ではありません。非メンバ関数は CLS 互換ではないので、代用として abstract sealed クラスを使うことができ、そこに静的メンバ関数を含めることができます。これはユーティリティ・クラス・パターンです。]
 abstract であり、sealed でもあるクラスは base-clause(基底節) やインスタンス・コンストラクタ、ないし、インスタンス・メンバを持つべきではありません。それはただ、静的メンバ、ネスト型、リテラル・フィールド、そして、typedef のみを持つべきです。
 abstract と sealed 修飾子は各々、§19.1.1.1 §19.1.1.2 で議論されています。

19.1.1.1 abstractクラス
 抽象クラスは標準C++の抽象クラス(§10.4 )のルールに従います。しかしながら、クラス修飾子 abstract を含むクラス定義は任意の抽象関数を含む必要はありません。[例:
struct B abstract {
    void f() { }
};
struct D : B { }

int main() {
    B b;    // エラー、B は抽象型
    D d;    // ok
}

 任意の抽象関数(アクセサ関数を含む)を含む ref クラスは明示的に abstract 宣言されるべきです。
 メタデータの詳細は、§34.7.1.1 を参照のこと。

19.1.1.2 sealedクラス
 sealed 修飾子はクラスからの継承を防ぐために使用されます。 もし、sealed クラスが他のクラスの基底クラスとして指定されていた場合、そのプログラムは不正です。[例:
struct B sealed {
};
struct D : B {    // エラー、sealed クラスからは派生できない
};

 クラスが sealed 指定されているか否かは、そのメンバ関数のいずれかがそれ自身 sealed されているかどうかには、なんの影響ももたらしません。
[注意:sealed 修飾子は原則的に予期せぬ派生を防ぐ目的で利用されますが、sealed 修飾子はまた、ある種の実行時最適化にも利用可能です。特に、sealed クラスはそれ以上、決して派生クラスも持たないことがわかっているので、仮想関数呼び出しのメンバをインスタンス化時に実関数呼び出しに置き換えることが可能となります。]
 メタデータの詳細は、§34.7.1.2 を参照のこと。

19.2 予約済み(Reserved)メンバ名

 背景となる C++/CLI ランタイムの実装を容易にするために、プロパティやイベントなどの CLI クラス型メンバ定義ごとに、実装はメンバ定義(§19.2.1 , §19.2.2 )の種類に基づいたいくつかの名前を予約するべきです。プロパティやイベントを宣言するクラスが、それらのプロパティやイベントの予約名のどれかと一致するメンバをを含んでいる場合、プログラムは不正です。
 名前検索の間、予約名は不可視です。
[注意:これらの予約名はいくつかの目的に奉仕しています。
  • get や set アクセサによって関数名として通常の識別子を使うことで他の言語と相互互換することを許すため。
  • CLI 標準のパーティションIは CLS-producer 言語のためにこれらの名前を勧告しているため。

 ファイナライザの CLI 概念と協調するために、関数として CLI クラス型中にいくつかの名前が予約されています。(§19.2.3

19.2.1 プロパティのために予約されているメンバ名

 スカラーないし、名前付きインデックス化プロパティ P(§19.5 )のために、以下の名前が予約されています。
get_P
set_P
 例え、スカラー・プロパティや名前付きインデックス化プロパティが読み込みのみ、書き込みのみだとしても、双方が予約されます。
[例:
ref struct A {
    property int P {
        int get() { return 123; }
    }
};
ref struct B : A {
    int get_P() {      // エラー
        return 456;
    }
};


 デフォルト・インデックス化プロパティ(§19.5 )のために、以下の名前が予約済みです。
get_Item
set_Item
 例え、デフォルト・インデックス化プロパティが読み込みのみ、書き込みのみだとしても、双方の名前が予約されます。
 デフォルト・インデックス化プロパティのデフォルト名後置子 Item は、そのプロパティの親の型に DefaultMemberAttribute (名前空間 System::Reflection から)を適用することによって変更することができます。クラス中の全てのデフォルト・インデックス化プロパティは同じ背景名を持つべきです。ひとたび、デフォルト・インデックス化プロパティの名前がこの方法で変更されたら、そのプロパティの親の型から派生した任意のクラス中で変更されるべきではありません。もし二つのインターフェイス・クラスがデフォルト・インデックス化プロパティを宣言しており、そして、それぞれがこの属性を通じて異なる名前を指定している場合、もし、その双方のインターフェイスの実装を型が宣言していれば、プログラムは不正です。
 他の手段として、プログラムはクラス中の全てのデフォルト・インデックス化プロパティに System::Runtime::CompileServices::IndexerNameAttribute を適用することでデフォルト名後置子を変更することができます。結果となるメタデータは IndexerNameAttribute を DefaultMemberAttribute(§34.7.5 参照)で置き換えることでしょう。同じメンバにデフォルト名後置子を指定するために、IndexerNameAttribute と DefaultMemberAttribute の双方を使うプログラムは不正です。同様に、同一クラス中の二つのデフォルト・インデックス化プロパティに違う背景名を IndexNameAttribute を指定するために使うプログラムは不正です。クラス中の全てのデフォルト・インデックス化プロパティには同じ IndexerNameAttribute が適用されるべきです。 [原理:C++/CLI では IndexerNameAttribute をいくつかの他の他言語に使用されるためのアプローチとしてサポートしており、メタデータ中で実際に発行されるものとして DefaultMemberAttribute をサポートしています。]
 メタデータの詳細は、§34.7.5 を参照のこと。

19.2.2 イベントのために予約されているメンバ名

 イベント E(§19.6 )に、以下の名前が予約済みとなっています:
add_E
remove_E
raise_E

19.2.3 関数のために予約されているメンバ名

 CLI クラス型のために、次の関数名とパラメータ・リストのコンビネーションが予約済みとなっています( T は任意の ref クラスです):
Dispose()
Dispose(bool)
Finalize()
__identifier("~T")()
__identifier("!T")()

19.2.4 衝突しうる予約済みプロパティ名とイベント名

 任意の与えられたプロパティやイベントのための予約済み名称のパターンはそのプロパティやイベントを定義するクラスでのみ予約されています。
[例:プログラム、
ref struct B {
    int get_X() { Console::WriteLine("B::get_X"); return 1; }
};
ref struct D : B {
    property int X {
        int get() { Console::WriteLine("D::X::get"); return 2; }
    }
};
int main() {
    D d;
    d.get_X();
}
は "B::get_X" を表示します。
 もし、プロパティやイベントが virtual であり、基底クラスが同名の virtual なプロパティやイベントを持っていれば、プロパティのために生成された背景のアクセサ関数は関数として導入されます。 それは、それらは基底クラスの関数を上書きしないと言うことです。プログラム、
ref struct B {
    virtual int get_X() { Console::WriteLine("B::get_X"); return 1; }
};
ref struct D : B {
    virtual property int X {
        int get() { Console::WriteLine("D::X::get"); return 2; }
    }
};
int main() {
    D d;
    d.get_X();
}
は "B::get_X" を表示します。 D から派生した時 B::get_X を上書きするただ一つの方法は名前付け override を使うことです。]
 もし、派生されたクラス中のプロパティやイベント・アクセサ以外の関数が基底クラスからの仮想アクセサ関数を上書きする場合、そのプログラムは不正です。 これらの関数は new 関数修飾子でマークされるべきです。これは基底クラスのアクセサ関数の名前が正規な get_X, set_X, add_X, remove_X、ないし、raise_X の名前を使っていなくても、真です。(これは C++/CLI 以外の言語で生成されたアセンブリを #using するときにのみ発生します。)[例:
ref struct B {
    virtual property int X {
        int get() { Console::WriteLine("B::X::get"); return 1; }
    }
};
ref struct D : B {
    virtual int get_X() new { Console::WriteLine("B::get_X"); return 2; }
};
int main() {
    D d;
    d.get_X();
}
 D::get_X に new キーワードが適用されていなければ、プログラムは不正です。]

19.3 データメンバ

 ref ないし、値クラス型は StructLayoutAttribute 属性(名前空間 System::Runtime::InteropServices )を持つことができます。 この属性はデータ構造のレイアウト、アライメント、サイズ、そして、文字列のマーシャリングを指定するために使われます。 インスタンス・データ・メンバは FieldOffsetAttribute 属性(名前空間 System::Runtime::InteropServices )を持つことができ、それはメンバの厳密な配置を制御します。(より詳細な情報は CLI 標準を参照のこと。) [例:
using namespace System::Runtime::InteropServices;
[StructLayout(LayoutKind::Explicit)]
public value class S1 {
    [FieldOffset(0)] int v;
    [FieldOffset(4)] unsigned char c;
    [FieldOffset(8)] int w;
};
[StructLayout(LayoutKind::Sequential, Pack=4)]
public value class S2 {
    int v;
    unsigned char c;
    int w;
};
[StructLayout(LayoutKind::Explicit, Size=12, CharSet=CharSet::Unicode)]
public value class S3 {
    [FieldOffset(0)] int* pi;
    [FieldOffset(0)] unsigned int ptrValue;
};
// S3 は union のように振る舞うことを意図しており、その様に取り扱われるべきである。

 データ・メンバはそれらに MarshalAsAttribute 属性(名前空間 System::Runtime::InteropServices 中)を適用させることができます。 この属性の情報は、§18.5 を参照のこと。
 メタデータの詳細は、§34.7.3 参照のこと。

19.4 関数

 関数定義に属性を許すために、function-definition (関数定義)§8.4 )のための標準C++ 文法は次のように拡張されます。
function-definition:
  attributesopt decl-specifier-seqopt declarator function-modifiersopt override-specifieropt ctor-initializeropt function-body
  attributesopt decl-specifier-seqopt declarator function-modifiersopt override-specifieropt function-try-block
 上書き指定子と関数修飾子の追加は function-definition (関数定義)member-declarator (メンバ宣言子) の生成物の一つのための標準 C++ 文法に変更を要請します。 [注意:二つの新しいオプション的構文生成物、function-modifieroverride-specifier、はその順に exception-specification の後、function-bodyfunction-try-block の前に現れます。]
 定義ではない関数宣言に属性、関数修飾子、そして、上書き指定子を許すために、member-declarator§9.2 )のための標準C++構文の生成物の一つは次のように拡張されます。
member-declarator:
  declarator function-modifiersopt override-specifieropt
  declarator constant-initializeropt
  identifieropt : constant-expression

function-modifiers:
  function-modifiersopt function-modifier

function-modifier:
  abstract
  new
  override
  sealed
 定義ではない関数宣言への属性のセットは対応する関数定義への属性のセットのサブセットであるべきです。属性は §29 に記述されています。
 function-modifier(関数修飾子) は次の節で議論されています。:abstract は §19.4.3 、new は §19.4.4 、override は §19.4.1 、sealed は §19.4.2 override-specifier(上書き指定子)§19.4.1 で議論されています。
 任意の function-modifier(関数修飾子), abstract, override, sealed、ないし、override-specifier(上書き指定子) を含むメンバ関数宣言は、明示的に virtual 宣言されるべきです。[原理:この新しい構文の主たるゴールは、プログラマに上書きをより明示的にすることで、そして、静かな上書きをより少なくすることで、その意図を明白にさせることです。virtual キーワードが、標準C++ は virtual キーワードをオプションとしているための後方互換性が必要な場合を除いて、全ての仮想関数に必要となります。]
 もし、関数が abstract と sealed 双方の修飾子を含んでいれば、もしくは、new と override の双方の修飾子を含んでいれば、その関数は不正です。
 クラス外メンバ関数定義は function-modifier(関数修飾子)override-specifier(上書き指定子) を含むべきではありません。
 もし、デストラクタやファイナライザ(§19.13 )が override-specifier(上書き指定子) を含んでいたら、もしくは、new か sealed function-modifier(関数修飾子) を含んでいたら、そのプログラムは不正です。

 parameter-decralation-clause(パラメータ宣言節)の標準 C++ 構文(§8.3.5 )はパラメータ配列を渡すことのサポートをするために、次のように拡張されています。
parameter-declaration-clause:
  parameter-declaration-listopt ...opt
  parameter-declaration-list , ...
  parameter-array
  parameter-declaration-list , parameter-array
 与えられた関数やインスタンス・コンストラクタにはパラメータ配列はただ一つ存在するべきであり、常に最後のパラメータとして指定されているべきです。

 パラメータ配列は §18.4 で議論されています。
 メタデータの詳細は、§34.7.4 を参照のこと。

19.4.1 関数の上書き(オーバーライド)

 direct-declarator (直接宣言子) のための標準 C++ 文法は上書き指定子同様、override 関数修飾子を許すよう拡張されています。(§19.2.3
override-specifier:
  = overridden-name-list
  pure-specifier

overridden-name-list:
  id-expression
  overridden-name-list , id-expression
 標準C++ では、基底クラス中にの仮想関数の cv-修飾 、パラメータ型リスト、そして、同じ名前を持つ関数が派生クラスに与えられていると、例え派生クラスの関数が virtual 宣言されていなくても、派生クラス関数は常に基底クラス中のそれを上書きします。 これは implicit overriding (暗黙の上書き) として知られています。ref クラスや値クラスに暗黙の上書き関数を含むプログラムは不正です。 [注意:プログラマは以下に記述されているように、明示的、ないし、名前付け上書きを使うことによって診断を削除することができます。]
 関数修飾子 override と上書き指定子の追加によって、C++/CLI は explicit overriding (明示的上書き)named overriding (名前付け上書き) を指定する能力を各々提供します。
 もし、function-modifier (関数修飾子) override か override-specifier (上書き指定子) のどちらかが派生クラスの関数宣言中にある場合、暗黙の上書きは一つも存在できません。[例:
ref struct B {
    virtual void F() {}
    virtual voidF(int i) {}
};
ref struct D1: B {
    virtual void F() override {}     // 明示的上書き B::F()
};
ref struct D2: B {
    virtual void F() override {}     // 明示的上書き B::F()
    virtual void G(int i) = B::F {}  // B::F(int) の名前付き上書き
};
ref struct D3: B {
    virtual void F() new = B::F {}   // B::F() の名前付き上書き
};

[注意:function-modifier (関数修飾子) override か override-specifier (上書き指定子) を含むメンバ関数宣言は、明示的に virtual (§19.2.4 )宣言されるべきです。]
 override-specifier (上書き指定子) は、基底クラスから上書きされるための直接的な、ないし、非直接的な一つ以上の仮想関数を指定する、コンマ切りされた名前のリストを含みます。
 上書き名を指し示す id-expression (id 演算) は上書きされる一つの関数を指し示します。id-expression(id 演算)で与えられた名前の検索はそれを含むクラスから始まります。[注記:もし、id-expression(id 演算)が非修飾名であり、その含むクラスが同じ名前の関数を持っていたら、プログラムは不正です。同じクラス中で関数の上書きは不可能です。]さらに、もし、基底クラス名が曖昧であれば、修飾が必要です。その関数は上書き関数として同じパラメータ型リストと cv-修飾を持つべきであり、二つの関数の返却型は同じであるべきです。
[例:
interface class I {
    void F();
};
ref struct B {
    virtual void F() { ... }
};
ref struct D : B, I {
    virtual void G() = B::F, I::F { ... }  // B::F と I::F を上書き
};
 B::F と I::F は別々にリストされなければなりません。もし、名前付け上書きが F だけ ]
[注意:同じ上書き振る舞いが時々違った方法で得られます。 例えば、与えられた基底クラス A が仮想関数 f を持っていたとして、上書き関数は override-specifier (上書き指定子) A::f を持っているかもしれませんし、 なんの override specifier (上書き指定子) や override function modifier (関数修飾子) も持たなかったり、function-modifier (関数修飾子) override を持っていたり、override A::f といった二つの結合だったりします。その全てが A::f を上書きします。]
 上書き関数の名前は上書きされるものと同じである必要はありません。
 派生クラスは一度より多く同じ仮想関数を上書きするべきではありません。 もし、名前付け上書きとして上書きが暗黙のうちに、明示的に、同じものになされようとしたら、そのプログラムは不正です。[例:
interface struct I {
    void F();
};
ref struct B {
    virtual void F() { ... }
    virtual void G() { ... }
};
ref struct D : B, I {
    virtual void G() = B::F { ... }
    virtual void F() {}   // エラー、B::F と I::F を上書きしようとしたが、
                          // B::F はすでに G に上書きされている
};

 もし、クラスが複数の関数を同じ名前、パラメータ型リスト、そして、cv-修飾で持っていたら、例え、それらは異なる継承した仮想関数を上書きしようとも、そのクラスは不正です。[例:
 ref struct D : B, I {
     virtual void F() = B::F { ... }  // OK
     virtual void F() = I::F { ... }  // エラー、宣言重複
 };

 関数は隠蔽と上書きの双方を同時に行うことができます。[例:
interface struct I {
    void F();
};
ref struct B {
    virtual void F() { ... }
};
ref struct D : B, I {
    virtual void F() new = I::F { ... }
};
 new 関数修飾子(§19.4.4 )の存在は D::F はその基底クラスやインターフェイスからのどんなメソッド F も上書きすることはないことを示しています。 名前付け上書きはその時 D::F は実際にただ一つの関数 I::F を上書きすると言うことになります。]
[注記:上書き指定子はそのクラスのその名前を導入しません。]
[例:
interface struct I {
    virtual void V();
};
ref struct R {
    virtual void W() {}
};
ref struct S : R, I {
    virtual void F() = I::V, R::W {}
};
ref struct T : S {
    virtual void G() = I::V {}
    virtual void H() = R::W {}
};
void Test(S^ s) {    // s はある S か T か何かを参照できる
    s->W();          // OK 仮想呼び出し
    s->R::W();       // R::W の非仮想呼び出し
    s->S::W();       // R::W の非仮想呼び出し
    s->S::F();       // OK (S から派生したクラスはこうする必要があり、この場合には曖昧さがない)
}
int main() {
    Test(gcnew S);
    Test(gcnew T);
}

 ジェネリック ref クラス(§31.1 )で仮想関数を上書きする目的でシグネイチャのマッチングを行う時、ないし、インターフェイスから関数を実装する時、型パラメータの拘束は考慮されません。型パラメータの拘束は異なることが可能です。[例:次のプログラム、
public interface struct P {};
public interface struct Q {};
public ref class PQ : P, Q {};

generic<typename T>
where T : P
public ref struct B {
    virtual void F(T) { console::WriteLine("B::F"); }
};
generic<typename T>
where T : P, Q
public ref struct D : B<T> {
    virtual void F(T) override { Console::WriteLine("D::F"); }
};
int main() {
    B<PQ^>^ b = gcnew D<PQ^>;
    b->F(gcnew PQ);
}
は "D::F" を表示します。D<T>^ は B<T>^ へのハンドル変換関数を、ただ、T が同じである場合、持っているので、上書き仮想関数が上書きする関数(それはただ反供変性を持つパラメータで上書きする型安全性があり)にパラメータの供変性を持っている時、パラメータは同じであるはずなので、型安全性はありません。]
 メタデータの詳細は、§34.7.4.1 を参照のこと。

19.4.2 sealed 関数修飾子

 function-modifier (関数修飾子) sealed でマークされた仮想メンバ関数は派生クラスで上書きされることはできません。[例:
struct B {
    virtual int f() sealed;
    virtual int g() sealed;
};
struct D : B {
    virtual int f();      // エラー:sealed 関数は上書きできない
    virtual int g() new;  // okay : B::g を上書きしない
};

[注意:function-modifier sealed を含むメンバ関数宣言は明示的に virtual 宣言されるべきです。]もし、基底クラスを暗黙のうちに上書きする virtual 関数がなければ、派生クラスは仮想関数を導入し、それを封じます。
 クラスのどのメンバ関数が封じられたかどうかは、クラスそれ自身が selaed かどうかということとは、なんの影響もありません。
 暗黙的な、明示的な、ないし、(CLI クラス型中で)名前付きの、上書きは基底クラスの少なくとも一つに非sealed仮想関数があるのと同じぐらい長く継続できます。 [例: A::f が sealed で、B::f がそうでない場合について考えてみます。もし、C が A と B から継承され、f を実装しようと試みた場合、それは成功しますが、ただ B::f についてのみ上書きされるでしょう。]
 メタデータの詳細は、§34.7.4.2 を参照のこと。

19.4.3 abstruct 関数修飾子

 標準 C++ では pure-specifier (純粋仮想指定子) を使って、仮想メンバ関数を抽象宣言することを許可しています。C++/CLI では function-modifier (関数修飾子) abstract を通すもう一つのアプローチを提供します。二つのアプローチは等価で、双方を一緒に使うことは正しい形式ですが、冗長です。[例:クラス shape は次の方法のどれででも抽象関数 draw を宣言することが可能です。
virtual void draw() = 0;           // 標準C++スタイル
virtual void draw() abstract;      // 関数修飾子スタイル
virtual void draw() abstract = 0;  // オーケイ、だけど、冗長。

[注意:function-modifier (関数修飾子) abstract を含むメンバ関数宣言は virtual 宣言されるべきです。]
 メタデータの詳細は、§34.7.4.3 を参照のこと。
 双方の抽象関数の親クラスのメタデータ実装は、§34.7.1.1 を参照のこと。

19.4.4 new 関数修飾子

 関数は new 関数修飾子を持つために virtual 宣言される必要はありません。 もし、関数が virtual 宣言され、そして、new 関数修飾子を持っていたら、その関数は他の関数を上書きしません。 しかしながら、CLI クラス型のために、名前付け上書きで他の関数を上書きすることが可能です。 virtual 宣言されておらず、new 関数修飾子でマークされた関数は virtual になることはなく、どんな関数にも暗黙のうちに上書きすることはありません。
[例:
ref struct B {
    virtual void F() { System::Console::WriteLine("B::F"); }
    virtual void G() { System::Console::WriteLine("B::G"); }
};
ref struct D : B {
    virtual void F() new { System::Console::WriteLine("D::F"); }
};
int main() {
    B^ b = gcnew D;
    b->F();
    b->G();
}
 生成される出力は
B::F
B::G
 次の例では隠蔽と上書きが一緒に発生します。
ref struct B {
    virtual void F() {}
};
interface class I {
    void F();
};
ref struct D : B, I {
    virtual void F() new = I::F {}
};
 new 関数修飾子の存在は D::F が基底クラスからのどのメソッド F をも上書きしないことを示しています。 名前付き上書き(§19.4.1 )はその時、D::F が実際にただ一つの関数 I::F のみを上書きすることを告げています。 その結果として I::F は上書きされますが、B::F は上書きされません。

 静的関数は継承したメンバを隠蔽するために new 関数修飾子を使うことができます。[例:
ref class B {
public:
    virtual void F() { ... }
};
ref class D : B {
public:
    static void F() new { ... }
};

 メタデータの詳細は、§34.7.4.4 を参照のこと。

19.5 プロパティ

 property (プロパティ) はフィールドであるかのように振る舞うメンバです。プロパティには2種類あります。スカラーとインデックス化です。scalar property (スカラ・プロパティ) はクラス・オブジェクトへのフィールド的なアクセスを可能にします。 スカラ・プロパティの例には、文字列の長さ、フォント・サイズ、ウィンドウ・キャプション、そして、顧客の名前、が含まれます。indexed property (インデックス化プロパティ) は CLI ヒープ・ベース・オブジェクト(しかし、クラスではない)に配列的なアクセスを可能にします。 インデックス化プロパティの例ではビット配列クラスです。
 プロパティはフィールドの革新的な拡張です--双方が関連する型で名前づけられたメンバであり、そして、スカラ・フィールドへのアクセス構文とスカラ・プロパティへのアクセス構文が同一で、同様に、CLI 配列へのアクセス構文とインデックス化プロパティへのアクセス構文も同じです。 しかしながら、フィールドと異なり、プロパティは格納位置を意味しません。 その代わり、プロパティはそれらの値が取得・設定される時実行されるべき命令文を指定する access function (アクセス関数) を持ちます。
 プロパティは property-definition (プロパティ定義) を使って定義されます。
property-definition :
  attributesopt property-modifiersopt property type-specifier-seq declarator property-indexesopt     { accessor-specification }
  attributesopt property-modifiersopt property type-specifier-seq declarator ;

property-modifiers:
  property-modifiersopt property-modifier

property-modifier:
  static
  virtual

property-indexes:
  [ property-index-parameter-list ]

property-index-parameter-list:
  type-id
  property-index-parameter-list , type-id
 property-definition (プロパティ定義)attribute (属性)§29 )、property-modifiers (プロパティ修飾子群)§19.5.2 §19.5.4 )、そして、property-indexes (プロパティ・インデックス) のセットを含むことができます。  property-indexes (プロパティ・インデックス) を含まない property-definition (プロパティ定義) はスカラ・プロパティです。 そして、property-indexes (プロパティ・インデックス) を含む property-definition (プロパティ定義) はインデックス化プロパティです。
 ( accessor-specification (アクセサ規定)は括弧で区切られるのと反対に )セミコロンで終わるスカラ・プロパティの property-definition (プロパティ定義) は、trivial scalar property (トリビアル・スカラ・プロパティ)§19.5.5 )を定義します。[注意:トリビアル・インデックス化プロパティのようなものはありません。]
 static 修飾子がデフォルト・インデックス化プロパティ定義に適用されるべきではないということをただ一つの例外に、プロパティ定義は修飾子の正規な組み合わせによって関数宣言するのと同じルールに従います。(デフォルト・インデックス化プロパティはこの節の後ろで紹介されています。)
 property-definition (プロパティ定義) は static ないし、virtual の property-modifier (プロパティ修飾子) を含む時、その修飾子は実際にプロパティのアクセサ関数の全てに適用されます。それら同じ修飾子をそのアクセサ関数に記述することは許容されていますが、冗長です。
 スカラ・プロパティ定義の type-specifier-seqdeclarator (宣言子) は、定義によって導入されたスカラ・プロパティの型を指定し、declarator (宣言子) はそのスカラ・プロパティの名前を指定します。インデックス化プロパティ定義の type-specifier-seqdeclarator (宣言子) は、定義によって導入されたインデックス化プロパティの要素の型を指定します。 [注意:あるプロパティ型(関数へのポインタや配列のポインタ)は直接的にプロパティ定義には記述できません。これらは最初に typedef として記述し、プロパティ定義で使われる時には同義型で記述するべきです。] スカラ・プロパティの型とインデックス化プロパティの要素型は関数にパラメータとして許されたからであるべきです。[注記:ネイティブ配列は関数パラメータとして許されていないので、それは同様にプロパティの型として許されていません。]
 declarator (宣言子) 中の identifier (識別子) はプロパティの名前を指定します。インデックス化プロパティには、もし、default が identifier (識別子) の代わりに使われていたら、そのプロパティは default-indexed property (デフォルト・インデックス化プロパティ) です。そうでなければ、プロパティは named indexed property (名前付きインデックス化プロパティ) です。
 accessor-specification (アクセサ指定) はプロパティのアクセサ関数(§19.5.3 )を宣言します。アクセサ関数はプロパティの読みとり・書き込みに関連した実行可能命令文を規定します。アクセサ関数は、プロパティ名によって修飾され、クラスのメンバであると見なされます。default-indexed property (デフォルト・インデックス化プロパティ) は、親のプロパティ名は default です。それゆえ、このインデックス化プロパティのアクセサ関数の完全名は default::get と default::set です。
 プロパティ・アクセサ関数は適切な型付けされたデリゲートに拘束されることができます。異なる property-index-parameter-list (プロパティ・インデックス・パラメータ・リスト) によるインデックス化プロパティの多重定義は許されています。インデックス化プロパティを含むクラスは同じ名前のスカラ・プロパティを含むことが可能です。
 クラス中にプロパティが存在することは、そのクラスを非 POD としません。
 参照型である型を持つプロパティは CLS-compliant ではありません。
 プロパティ演算は、もし、その get アクセサ関数が左辺値、ないし、gc 左辺値を返すなら、各々、左辺値、ないし、gc 左辺値です。そうでなければ、プロパティは右辺値です。
 メタデータの詳細は、§34.7.5 を参照のこと。

19.5.1 プロパティとイベントの修飾名

 C++/CLI 中の修飾名はプロパティとイベントを含むことができます。これを協調的に行うために、C++ 文法は次のように拡張されます。
property-or-event-name :
  identifier
  default

unqualified-id:
  identifier
  operator-function-id
  conversion-function-id
  ~ class-name
  ! class-name
  template-id
  generic-id
  default

class-or-namespace-name:
  class-name
  namespace-name
  property-or-event-name
 もし、qualified-id (修飾ID)nasted-name-specifier (ネスト化名指定子) がプロパティかイベントを指定したら、nasted-name-specifier (ネスト化名指定子) の後に指定した名前はアクセサ関数であり、プロパティかイベントのスコープ中で検索されます。
 declarator (宣言子) 中ではdefault キーワードはデフォルト・インデックス化プロパティを宣言する時にのみ使われるべきです。default キーワードは演算中で postfix-expression (後置演算) がデフォルト・インデックス化プロパティを評価している時にのみ使われるべきです。[注意:構文上は default キーワードを identifier (識別子) が変数名や関数名として許されている場所に許しているので、これらのルールはデフォルト・インデックス化プロパティ中で使われる default の使い方を制限しています。]
 もし、アクセサ関数の定義がそのプロパティやイベント定義の外側の文脈にある場合、そのアクセサ関数の名前は :: 演算子を使ってプロパティやイベントによる修飾をされるべきです。そうでなければ、プロパティとイベントのアクセサ関数の定義や宣言のルールはクラスのメンバ関数のルールと同じです。

19.5.2 static とインスタンス・プロパティ

 プロパティ定義が static 修飾子を含む時、プロパティは static property (静的プロパティ) であると言います。[注意:デフォルト・インデックス化プロパティは static にはなれません。]static 修飾子が付いていない時には、そのプロパティは instance property (インスタンス・プロパティ) であると言います。 静的プロパティの全てのアクセサ関数は static であり、インスタンス・プロパティの全てのアクセサ関数はインスタンス・アクセサ関数です。[例:
ref struct C {
    static property C^ MyStaticProperty { ... }  // 静的プロパティ
    property int default[int] { ... }:           // インスタンス・プロパティ
};

[注意:フィールドのように、静的プロパティが E::M の形式を使って参照されている時、E はプロパティ M を持つ型を意味するべきです。インスタンス・プロパティが E.M の形式を使って参照されている時、E はプロパティ M が持つインスタンスを意味するべきです。インスタンス・プロパティがポインタやハンドルを通して参照されている時、E->M 形式が使われます。]

19.5.3 アクセサ関数

 プロパティのaccessor-specification (アクセサ規定) はプロパティの読み書きに関連した実行可能命令文を指定します。
accessor-specification:
  accessor-declaration accessor-specificationopt
  access-specifier : accessor-specificationopt

accessor-declaration:
  attributesopt decl-specifier-seqopt member-declarator-listopt ;
  function-definition
 属性は §29 に記述されています。関数定義は §19.4 です。
 プロパティの再書き込みとアクセサ関数演算中のイベント演算のルールは §15.14 でカバーされています。
 プロパティは少なくとも一つのアクセサ関数を持つべきです。プロパティのアクセサ関数の名前は get (これを get accessor function とします)ないし、set (これを set accessor function とします)のどちらかであるべきです。 プロパティは一つより多くの get アクセサ関数を持つべきではなく、一つより多くの set アクセサ関数を持つべきではありません。 プロパティのアクセサ関数はプロパティ定義中にインライン定義でき、また、クラス外に定義できます。
 プロパティが cv-qualified であるアクセサ関数を含んでいたり、その最後か唯一のパラメータがパラメータ配列であるプログラムは不正です。
 もし、アクセサ関数が abstract 宣言されていなければ、それは定義されるべきものです。
 スカラ・プロパティの get アクセサ関数はパラメータを取らず、プロパティの型に厳密に一致するべき型 type-specifier-seq を返します。 インデックス化プロパティには、get アクセサ関数のパラメータはプロパティの property-indexes の型に厳密に対応するべきです。
 スカラ・プロパティの get アクセサ関数はプロパティの型 type-specifier-seq に厳密に対応した一つのパラメータを持ちます。 インデックス化プロパティには、set アクセサ関数のパラメータはプロパティの property-indexes の型に厳密に対応し、最後のパラメータに従われ、その最後のパラメータはプロパティの型 type-specifier-seq に厳密に対応しているべきです。スカラとインデックス化の双方のプロパティの set アクセサ関数の返却型は void であるべきです。
 get と set アクセサ関数の存在と不在に基づいて、プロパティは次のように分類されます。
  • get アクセサ関数と set アクセサ関数の双方を含むプロパティは read-write (読み書き) プロパティであると言います。
  • get アクセサ関数のみを持つプロパティは read-only (読み込み) プロパティであると言います。
  • set アクセサ関数のみを持つプロパティは write-only (書き込み) プロパティであると言います。

 全てのクラス・メンバ同様、プロパティは明示的、暗黙的な access-specifier (アクセス指定子) を持ちます。 どちらか、ないし双方のプロパティ・アクセス関数もまた access-specifier (アクセス指定子) を持つことができ、そのアクセス関数にはプロパティのアクセス可能性とよりアクセスし難い指定がされます。 アクセサ関数の access-specifier (アクセス指定子) はこれらのアクセス関数にのみアクセスを指定します。 それらは親プロパティに続く親クラス中のメンバのアクセス可能性にはなんの効果も持ちません。 プロパティに続くアクセス可能性はプロパティの前のアクセス可能性と同じです。
[例:
public ref class Button : Control {
private:
    String^ caption;
public:
    property String^ Caption {
        String^ get() {
            return caption;
        }
        void set(String^ value) {
            if (caption != value) {
                caption = value;
                Repaint();
            }
        }
    }
};
 ボタン・コントロールは public Caption プロパティを宣言しています。このプロパティはプロパティがセットされた時を除いて、その場合コントロールは与えられた新しい文字列を再描画しますが、フィールドに蓄えられている文字列を返す以外には何もしません。
 上で与えられたボタン・クラスは次のように Caption プロパティの使用例があります。
Button^ okButton = gcnew Button;
okButton->Caption = "OK";          // set アクセサ関数呼び出し
String^ s = okButton->Caption;     // get アクセサ関数呼び出し
 ここで、set アクセサ関数はプロパティに値の代入時に呼び出され、そして、get アクセサ関数は演算でプロパティに参照されることによって呼び出されます。]
[注意:プロパティを通して状態をさらすことはフィールドを直接的にさらすことに比べてより有効性が低い必要はありません。特に、プロパティへのアクセスはプロパティのアクセサ関数を呼び出すことと同じです。適切な時、実装はこれらの関数呼び出しをインライン化できます。プロパティの使用はクラスのいくつかのバージョンを通してバイナリ互換性を維持していくのに有効なメカニズムです。]

 アクセサ関数はインライン、ないし、クラス外に定義されることができます。[例:
public ref class Point {
private:
    int x;
    int y;
public:
    property int X {
        int get() { return X; }               // インライン定義
        void get(int value);                  // 宣言のみ
    }
    property int Y {
        int get();                            // 宣言のみ
        void set(int value) { y = value; }    // インライン定義
    }
    ...
};
void Point::X::set(int value) { x= value; }
int Point::Y::get() { return y; }


19.5.4 virtual、sealed、abstract、そして、アクセサ関数の上書き(オーバーライド)

 sealed されたアクセサ関数は virtual も宣言されるべきです。sealed 修飾子は派生クラスがアクセサ関数を上書きすることを防ぎます。
 abstract 修飾子を持つアクセサ関数は abstract であり、それを含むクラスの抽象関数と同じルールに従います。 abstract なアクセサ関数は virtual もまた宣言されるべきです。
[例:
ref struct B abstract {
    property String^ Name {  // Name は virtual
        virtual String^ get() abstract;
    }
};
ref struct D : B {
    property String^ Name {  // Name はここで sealed になる
        virtual String^ get() override sealed { ... }
    }
};


 インターフェイス中で定義された任意のプロパティは暗黙のうちに abstract です。 しかしながら、これらのプロパティは virtual と/か abstract 修飾子と pure-specifier (純粋指定子) を冗長ですが含むことができます。[例:
interface class X {
    property int Size;      // (暗黙的な) 抽象プロパティ
    property String^ Name {
        virtual String^ get() abstract = 0;
                            // "virtual", "abstract", "=0"
                            // は許されますが、冗長です
    }
};

 abstract 修飾子同様、override 修飾子や override-specifier を含むプロパティ定義は、プロパティが abstract であり、基底プロパティを上書きすることを指定します。
[注意:抽象プロパティ定義は抽象クラス(§19.1.1.1 )でのみ許されています。]
 継承した仮想プロパティのアクセサ関数は override 修飾子か override-specifier (上書き指定子)§19.4.1 )を指定するプロパティ定義を派生クラス中に含んでいることによって上書きされることができます。 これは overriding property definition (上書きプロパティ定義) として知られています。上書きに期待されているように、アクセサ関数はメンバ関数として同じ振る舞いを取ります。 [例:
ref struct B {
    property int Count {
        virtual int get() { ... }
    }
};
ref struct D : B {
    property int Count {
        virtual int get() override { ... }
    }
};

 アクセサ関数は他のプロパティのアクセサ関数を上書きできます。それは非アクセサ関数も上書きできます。[例:
ref struct B {
    property int Count {
        virtual int get() { return 0; }
        virtual void set(int val) { }
    }
    virtual int GetCount() { return 0; }
};
ref struct D : B {
    property int MyCount {
        virtual int get() = B::GetCount { return 0; }
    }
};

 上書きプロパティ定義は同じかより広いアクセス修飾子を持ち、継承したプロパティと厳密に同じ型で同じ名前であるべきです。 もし、継承されたプロパティが読み込みのみや書き込みのみのプロパティであれば、上書きプロパティは各々読み込みのみや書き込みのみ、ないしは、読み書きのプロパティであるべきです。 もし、継承したプロパティが読み書きプロパティであれば、上書きプロパティは読み書きプロパティであるべきです。
 トリビアル・スカラ・プロパティは他のプロパティを上書きするべきではありません。
 定義と呼び出しの構文の違いを除いて、virtual, sealed, override, abstract アクセサ関数は厳密に各々、virtual, sealed, override, abstract 関数であるかのように振る舞います。 特に、標準C++ (§10.3 )で記述されたルールとこの標準の §19.4.2 §19.4.1 、そして、§19.4.3 は、アクセサ関数が対応する形式の関数であるかのように適用されます。
[例:例、
ref class R abstract {
    int y;
public:
    virtual property int X {
        int get() { return 0; }
    }
    virtual property int Y {
        int get() { return y; }
        void set(int value) { y = value; }
    }
    virtual property int Z abstract {
        int get();
        void set(int value);
    }
};
 X は仮想読み込みのみプロパティで、Y は仮想読み書きプロパティで、Z は抽象読み書きプロパティです。]

19.5.5 トリビアル・スカラ・プロパティ

 トリビアル・スカラ・プロパティは property-definition (プロパティ定義) を(accessor-specification (アクセサ規定) が括弧で区切るのと反対に)セミコロンで終わらせることで定義されます。[例:
ref struct S {
    property int P;
};

 トリビアル・スカラ・プロパティは読み書き可能で、暗黙のうちにアクセサ関数が定義されています。これらのアクセサ関数のために暗黙のうちに与えられた access-specifier (アクセス指定子) は親プロパティのものと同じです。 トリビアル・スカラ・プロパティのプライベート背景ストレージは、実装に予約された場所にそのストレージの名前で自動的に領域確保されます。 暗黙的に定義された set アクセサ関数は与えられた値をプライベート背景ストレージにセットする以外のなんの目に見える振る舞いもするべきではありません。 暗黙的に定義された get アクセサ関数はプライベート背景ストレージの値を返す以外のなんの目に見える振る舞いもするべきではありません。
 トリビアル・スカラ・プロパティは static ないし、virtual になれます。
 トリビアル・スカラ・プロパティの型は参照型を持つべきでなく、また、cv 修飾されるべきではありません。

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