9.字句構造
9.1 トークン
9.1.1 識別子
標準C++ の文法ではしかるべき場所で識別子を受け入れてはいません。
しかし、C++/CLI は、特別な意味を持っているこれらの識別子によって、それらの場所での識別子のセットを定義することを許しています。
[注意:そのような識別子は口語的に文脈依存キーワードとして言及されています。そうでなければ、それらは識別子なのです。]
一定の文脈上で特殊な意味づけをもたらす識別子は次の通りです。
abstract | delegate | event | finally | generic | in |
initonly | internal | literal | override | property | sealed |
where |
文法上で参照されたとき、これらの識別子は
identifier文法生成を使うよりも明示的に使われます。
識別子が意味を持つことを保証するのは、構文上のチェックよりも、むしろ意味論的チェックになります。
トークンが識別子であると取られたときになんの適切なパースがなかったとき、その時にのみ、与えられたコンテキスト中において識別子はキーワードであるとみなされます。それはつまり、トークンが識別子になれるとき、それは識別子と
なります。
いくつかの名称パターンはしかるべきコンテキストの関数名として予約されています。(
§19.2 、
§19.7.5 )
トークン generic が見つかると、もし、トークン :: に先んじており、続けてトークン < 、そして、キーワード class か typename がある時のみ、generic は特別な意味を持つことになります。[注意:稀なケースですが、正規な標準 C++ プログラムとして、generic に続いて < 続いて class となるトークン列が含まれることが、generic が型の名前として記述されているような場合で、あり得ます。例えば、
template<typename T> struct generic {
typedef int I;
};
class X {};
generic<class X> x1;
generic<class X()> x2;
そのような場合には、generic が型の名前であることを示すよう、typename を使ってください。
typename generic<class X> x1;
typename generic<class X()> x2;
もしくは、これらの特殊な場合には、別の表記としてキーワード class を(
elaborated-type-specifier を使わないように)取り除いても良いでしょう。例えば、
generic<X> x1;
generic<X()> x2;
]
typename を言及する
elaborated-type-specifier(標準 C++ § 7.1.5.3、§ 14.6、§ A.6 ) の構文生成は、二つの適切な生成の最初のものを随意に
nested-name-specifier とするために、次のように拡張されています。
elaborate-type-specifier:
attributesopt class-key ::opt nested-name-specifieropt identifier
attributesopt class-key ::opt nested-name-specifieropt templateopt template-id
attributesopt enum-key ::opt nested-name-specifieropt identifier
attributesopt typename ::opt nested-name-specifieropt identifier
attributesopt typename ::opt nested-name-specifier templateopt template-id
[注意:標準 C++ の改訂が現在進んでおり、この生成物もその改訂の提出によって変更を加えます。]
attributes は
§29 に記述されています。
標準 C++ (§ 14.6/3)は以下のように訂正されます。
「ある型の、nested-name-specifierがテンプレート・パラメータ(14.6.2)に依存するqualified-ididentifierは、elaborated-type-specifier(7.1.5.3)を形成するべく、型を示すqualified-ididentifierを指し示すキーワード typename が前に置かれているべきである」
そして、§ 14.6/5 は削除されます。
「キーワード typename は、関数テンプレートの返却型中やメンバテンプレート関数、クラステンプレートのメンバ関数の返却型定義、クラステンプレート中のクラス、そして、クラステンプレートの静的メンバ、ネストクラスの定義中で型を指定するため、などのテンプレート宣言の中でのみ使われるべきである。キーワード typename は完全修飾名に対してのみあてがわれるべきであるが、それらの名前の依存性は必要でない。キーワード typename は依存名称が利用できる文脈中でのみ使うことができるべきである。これはテンプレート宣言と定義を含んでいるが、明示的な特殊化宣言やインスタンス化宣言は除く。キーワード typename は基本指定子やメモリ初期化子には含まれていない。それらの文脈中ではテンプレート・パラメータ(14.6.2)に依存する qualified-id は暗黙のうちに型の名前と仮定されている」
[注意:typename の存在は、指定をしないと property :: X x; のように曖昧になってしまう場合に、プログラマに曖昧さの排除を強いています。宣言 property :: X x; は標準C++ が認識するとおり、property::X 型のメンバ変数 x を宣言します。トークン列 property typename :: X x; は ::X という型のプロパティをxと言う名前で宣言します。]
array, interior_ptr, pin_ptr, または、safe_cast のどれかの名前解決を行ってその名前を見つけられなかったとき、そして、その名前に左括弧が続かなかった場合、その名前は cli:: で修飾されているものとして解釈され、名前空間 ::cli にその名前を見つけるべく名前解決を継続します。
array, interior_ptr, pin_ptr, または、safe_cast のどれかの名前解決を行ってその名前を名前空間 ::cli に見つけたとき、その名前は通常の識別子ではなく、この標準に記述されている特殊な意味を持っています。
識別子でないトークンは識別子として使うことも可能です。これは __identifier(
T) 、
T は識別子、キーワード、または文字列リテラルである、を通して仕立てます。文字列リテラル形式は C++/CLI 実装に使うために予約されています。この構成物は最初の、そして、ただ一つの識別子として #define プリプロセッサ命令中に配置するべきではありません。[例:
__identifier(totalCost)
__identifier(delete)
__identifier("<Special Name #3>")
]
9.1.2 キーワード
以下のキーワードが標準 C++ (§2.11 )に追加されています。
enum#class | enum#struct | for#each | gcnew |
interface#class | interface#struct | nullptr | ref#class |
ref#struct | value#class | value#struct | |
シンボル # はキーワード中に現れる空白を指し示す文法のために使われています。変換フェーズ1の後のプログラム・テキスト中に現れる任意の空白がシンボル # の場所に配置されることが許されています。シンボル # の場所に配置されることが許された空白がコメントやドキュメント・コメント、マクロによって生成されたものかどうかは問題ではありません。次に示す変換フェイズ4で # を含むトークンが一つのキーワードであるとされます。[注意:# シンボルは言語の文法の中でのみ使われています。例には正規なプログラムが要請している空白が含まれています。][注意:# シンボルを含むキーワードはマクロによって生成することができますが、決してマクロ名であるとは認識されておりません。]
標準C++ (§2.1/4 )の変換フェイズ4は以下のように拡張されています。
指示語のプリプロセス処理が実行されます構文解析され、蓄えられます。
そして、変換ユニット中の、各々のマクロの置き換えリスト中で、最初のトークンから始めて、token1 と token2 の適切なトークンの対が各々継続的に認識され、もし、token1 と token2 がキーワードであれば、token1#token2 の一語に置き換えます。終わりそして、マクロ呼び出しが展開されます。
もしユニバーサル・キャラクター名称の構文にマッチした文字列が、連動したトークン(16.3.3 )を生成すれのであれば、その振る舞いは未定義です。
#include プリプロセス指示語は再起的に名前を与えられたヘッダやソース・ファイルをフェイズ1からフェイズ4までを通して引き起こします。
文法中の多くの場所で、特定の識別子は特別な意味を持っていますが、キーワードではありません。[注意:例えば、仮想関数宣言の中で、識別子 abstract と sealed は特別な意味を持っています。通常、ユーザー定義識別子は決してそのような場所には認められていませんが、これはこれらの言葉を識別子として衝突することなく使用しています。これら特殊な識別子の完全なリストは
§9.1.1 を見てください。]
9.1.3 リテラル
標準 C++ のリテラルに関する文法(§2.13 )は以下のように拡張されています。
literal:
integer-literal
character-literal
floating-literal
string-literal
boolean-literal
null-literal
9.1.3.1 整数リテラル
long long int と unsigned long long int 型を協調して追加するために、標準 C++ (§2.13.1)中の整数後置子は以下のように拡張されます。
integer-suffix:
unsigned-suffix long-suffixopt
unsigned-suffix long-suffixopt
long-suffix unsigned-suffixopt
long-long-suffix unsigned-suffixopt
long-long-suffix: どちらか
ll LL
標準 C++ (§2.13.1/2)は以下のように変更されます。
「整数リテラルの型はその形、値、そして、後置子に依存します。もし、それが10進数で後置子をつけていなければ、整数リテラルの型はその値を表現できる型の最初のものを取ります:int, long int, long long int;もし、その値が long int として表現できなければ、その振る舞いは未定義となります。
もし、値が8進数か16進数であり、後置子がついていなければ、その値の型はその値を表現できる型の中で最初のものを取ります:int, unsigned int, long int, unsigned long int, long long int, unsigned long long int。
もし、値の後置子が u もしくは U であれば、その型は値を表現できる型の中の最初のものを取ります:unsigned int, unsigned long int, unsigned long long int。
もし値が十進数であり、後置子が l ないし L であれば、その型は値を表現できる型の中の最初のものを取ります:long int, unsigned long intlong long int。
もし、値が八進数ないし十六進数で、後置子が l ないし L であれば、その型は値を表現できる型の中の最初のものを取ります:long int, unsigned long int, long long int, unsigned long long int。
もし、値の後置子が ul, lu, uL, Lu, Ul, lU, UL もしくは LU であれば、その型は値を表現できる型の中の最初のものを取ります:unsigned long int, unsigned long long int。
もし値が十進数であり、後置子が ll ないし LL であれば、その型は long long int となります。もし、値が八進数ないし十六進数で、後置子が ll ないし LL であれば、その型は値を表現できる型の最初のものとなります:long long int, unsigned long long int。
もし、後置子が u ないし U、ll ないし LL の両方を持てば、その型は unsigned long long int を取ります。」
拡張整数型を協調して追加するために、標準 C++ (§2.13.1/3)は以下のように変更されます。
「もし、整数定数がそのリストのどのタイプでも表現できなかったず、拡張整数型がその値を表現できたのなら、それは拡張整数表現であるかもしれない。
もし、整数のためのリスト中の全ての型が符号付きであれば、拡張整数型は符号付きであるべきだろう。
もし、その定数のためのリストの型全てが符号なしであれば、拡張整数型は符号なしであるべきだろう。
もし、整数リテラルを含む変換単位の一つでも許された型のどれかで表現できない場合は、プログラムは不正となる。」
9.1.3.2 null リテラル
null-literal:
nullptr
null literal (null リテラル)は null 型(
§12.3.4 )であるキーワード nullptr です。
nullptr は
null 値定数を表しており、ユニーク(一意な)存在です。このリテラルは左辺値にはなりません。
null 値定数は、結果が null ハンドルとなるよう、任意のハンドル型に変換可能です。
null 値定数は、また、結果が null ポインタとなるよう、任意のポインタ型にも変換可能です。
9.1.3.3 文字列リテラル
標準 C++(§2.13.4/1)は以下のように変更されます。
「... 通常の文字リテラルは<narrow-string-literal-type>型を持ちます。
この型は言語によって表現することはできませんが、§14.2.5 に記述されているとおり、暗黙のうちに System::String^ ないし、const char n個の配列の双方に変換することが可能です。"const char n個の配列"と静的保存期間(3.7)は以下に定義される通り n は文字列のサイズで、与えられたキャラクタによって初期化されています。
... ワイド文字列リテラルは<wide-string-literal-type>型を持っています。
この型は言語として表現できませんが、§14.2.5 に記述されている通り、暗黙のうちに System::String^ ないし、const wchar_t の n個の配列に変換することができます。
"const wchar_t n 個の配列"と静的保存期間は、以下に定義される通り n は文字列のサイズで、与えられたキャラクタによって初期化されています。」
9.1.4 演算子と強調子
C++/CLI はテンプレートとジェネリック・コンストラクタ、List<List<int>> のような、>> が一つではなく二つのトークンである、表現が許されていることを要求しています。
これは標準 C++ でこれを仕様規定している節 §15.3 , §30.1 そして、§30.2 の数多くの部分への変更を要請します。
標準 C++ (§2.1/1 )、変換フェーズ7は、すでに存在する注記に優先して、以下のような文章を追加することが議論されています。
[注意:トークンの分析と翻訳の過程で、時折、ひとつのトークンを他のトークンのシーケンスによって置き換える結果となるかもしれない。( 14.2 )]