2009-09-21

user defined literalが予約語の制約を無視できる理由

2chで軽く論破されてしまった。こんな事に気がつかなかったとはなさけない。試験などで立て込んでいて、少々遅れたが、user defined literalが、予約語を無視できる理由について述べる。

アンダースコア二つから始まる名前と、アンダースコアひとつに大文字から始まる名前は、あらゆる利用を予約されている。
アンダースコアひとつで始まる名前は、グローバル名前空間において、予約されている。

予約されている名前を使うと、動作は未定義である。

ところで同時に、規格では、「アンダースコアひとつから始まらないuser defined literalのLiteral suffix identifierは、予約されている」と書かれている。つまり、user defined literalのLiteral suffix identifierは、アンダースコアひとつから始まらなければならないのだ。Literal suffix identifierとは、もちろんidentifierでもある。これは、予約語の制限と衝突する。これは一体、どう解釈すればいいのか。

実は、名前と識別子とは、同義ではない。名前の定義は、3 Basic conceptsに書いてある。

3 An entity is a value, object, variable, reference, function, enumerator, type, class member, template, template specialization, namespace, parameter pack, concept, or concept map.

4 A name is a use of an identifier (2.11), operator-function-id (13.5), conversion-function-id (12.3.2), or template-id (14.3) that denotes an entity or label (6.6.4, 6.1).

p4を一見すると、identifierはいかにもnameである。ところが、最後に奇妙な文がくっついている。"that denotes an entity or label"という文だ。どうやら、ただにidentifierなだけではnameにならず、entityやlabelでなければならないらしい。

labelというのは、gotoで飛ぶアレのことだから、識別子には当てはまらないが、entityとは、一体何だろう。entityはp3で規定されている。みると、値だとか、オブジェクトだとか、変数、参照、関数、列挙子、型、クラスメンバー、テンプレート、テンプレート特殊化、名前空間、パラメーターパック(Variadic Templateのこと)などなど。

ようするに、entityでなければ、nameではないということだ。予約語の制限は、nameに対して適用される。すべてのidentifierに対してではない。

さて、user defined literalは、現行ドラフトの、13.5.8 User-defined literalsに、以下のように定義されている。

literal-operator-id:
    operator "" identifier

user defined literalは、例えば、operator "" _x という形で参照しなければ、entityにならない。_xだけでは、entityではないのだ。entityでないということは、nameではない。nameではないということは、予約語の制約を受けない。

従って、以下の宣言はすべてwell-formedなC++0xのコードである。

// OK アンダースコアから始まっていてもよい。
int operator "" _x( const char* ) ;

// OK アンダースコアに大文字から始まっていてもよい。
int operator "" _X( const char* ) ;

// OK アンダースコア二つから始まっていてもよい。
int operator "" __x( const char* ) ;

ただ、問題はある。C++0xの規格が制定されたら、当然、実装が出回る。そうすると、皆C++0xの機能を使い出す。問題は、大部分のユーザーは、規格を読んでC++を学ぶのではない。なぜuser defined literalが、例外的に予約語の制限を免れるかという理由など、知るわけがない。

user defined literalの識別子は、アンダースコアから始まらなければならないが、果たしてユーザーは、その決まり事を守るかどうか。コンパイラがちゃんとエラーを出すかどうか。

user defined literalで、アンダースコアから始まる名前が使えること(というより、アンダースコアから始まらなければならない)を知ったユーザーは、果たして、それがuser defined literal限定であることも、学ぶかどうか。この問題を知らない人が、C++の参考書を書くかも知れない。ユーザーはそれを読んで、勘違いするかも知れない。今現在、ほとんどのコンパイラは、ユーザーが予約語を使ったからと言って、エラーはおろか、警告すら出したりしない。ユーザーが、名前はアンダースコアから始まっても良いのだと、勘違いしないだろうか。

アンダースコア二つ、あるいは、アンダースコアひとつに大文字で始まる名前は、あらゆる使用が予約されている。例えば、処理系独自の拡張機能を提供するキーワードなどだ。そして、大抵のコンパイラでは、そういうキーワードは、パースの時点で、特別に処理されるような実装になっていたりする。その手のキーワードが、識別子として使われている時点でエラーを出したりするかもしれない。これは規格違反だが、実際に多くのコンパイラがそういう実装になっている以上、無視するわけにはいかない現実だ。だから、アンダースコア二つ、あるいはアンダースコアひとつに大文字で始まるuser defined literalは、出来るだけやめた方がよい。

user defined literalがこのまま規格に残るのであれば、これらは気にしなければ行けない問題だ。願わくは、C++0xの参考書を書く人が、user defined literalを解説する時に、これらの問題も取り上げるべきだと思う。たとえ、細部に踏み込み、究極的な、「何故」にまで言及するような解説までは書かないとしても、user defined literalの識別子は、他の識別子とは違って特殊なので、これらの事に気をつけるべきだという、注意があってほしい。

「智慧」の人とか、「信実と勇気」の人は、おそらくはC++0x本を書くのではないかと思うが、もしuser defined literalを解説するのであれば、この辺のことにも言及しておいてほしい次第である。

1 comment:

Anonymous said...

> 2chで軽く論破されてしまった。
http://pc12.2ch.net/test/read.cgi/tech/1253280377/14-37