2009-03-17

ユーザー定義リテラルに関するポジティブな考察

思うに、自分は今まで、ユーザー定義リテラルのネガティブな面しか考えていなかったように思う。そこで、ポジティブな面について、考察してみることにした。まず、ユーザー定義リテラルは、単なるシンタックスシュガーであって、パフォーマンス上の利点はない。では、いったいどのように使えば、文法上の利点を得られるのか。

class gram ;

gram operator "" _g(unsigned long long int) ;
gram operator "" _kg(unsigned long long int) ;
gram operator "" _pond(unsigned long long int) ;
gram operator "" _kan(unsigned long long int) ;


void f()
{
    // ユーザー定義リテラルを使うと、とてもすっきり。
    auto weight = 643_g + 3_kg + 4451_pond + 320_kan ;

    // ユーザー定義リテラルがない場合の、実現可能な文法。なんだか読みづらい。
    // auto weight = g(633) + kg(3) + pond(4451) + kan(320) ;
}

オーケー、これはクールだ。このようなコードならば、ユーザー定義リテラルも使ってみたくなると言うものだ。

しかし、アンダースコアから始まらなければならないという制限はどうしたものか。どうやって解決すればいいのだろう。ダブルアンダースコア、あるいは、アンダースコアに大文字で始まる名前は予約されている。アンダースコアで始まる名前は、グローバル名前空間において予約されている。このままでは、グローバル名前空間にユーザー定義リテラルのオーバーロード関数を定義することは出来ない。従って、クラス内か、何らかの名前空間の中で定義しなければならないのだが、クラス内はともかくとして、具体的な名前空間の中でしか定義できないのは苦痛である。第一、どうやって使うのだ。

またもし、グローバル名前空間に定義できたとしても、名前の衝突をどうやって避ければいいのか。上記の例に挙げたような、SI単位などの有名な名前は、みんな使いたがるはずだ。それに、この文法の性質上、みんな数文字程度の短い名前を使うに決まっている。長ったらしい名前など使いたがるわけがない。従って、複数のライブラリで名前が衝突すると言うことが、実際に起こりうる。それに、みんなアンダースコアひとつという一番簡単な名前は、自分が使いたいはずだ。

これを延々と考えていたが、ついに以下のような結論に達した。規格は正しい。つまり、ユーザー定義リテラルは、グローバル名前空間に定義してはいけないのだ、という事だ。規格の定義より明らかに、何らかの名前空間の中に定義しなければならない。では、どうやって実際に使うのか。それは、using directiveを使うのだ。

namespace SI {

class gram ;
gram operator "" _g(unsigned long long int) ;

}

void f()
{
    using namespace SI ;

    gram weight = 100_g ;
}

うれしいことに、こうすれば、名前の衝突と、名前の予約の問題を、同時に回避できる。名前の衝突は、その関数内だけを考えればよく、名前の予約の問題も、その関数の中だけで、グローバル名前空間には影響を及ぼさないので、問題はない。

規格を読む限り、well-formedにするには、こうするしかないと思われる。このことは一切説明されてはいないのだけれど。

追記:
using namespace hogehogeとやるほうが、using directiveであり、それではない方は、using declarationであった。名称を逆に覚えていたらしい。

7 comments:

xyuyux said...

using namespace SI;
or
using SI::operator "" _g;
やっぱりこう書くしかないですよねー。

hito said...

using directiveは、その記述の煩雑さから、実際のコードでは、まずこれには使われないと思いますけどね。

DigitalGhost said...

using directive と using declaration が逆ですよ。

hito said...

おっと。逆だった。

r said...

トリプルアンダースコアで始まれば良いんですね、分かります

hito said...

トリプルアンダースコアも、ダブルアンダースコアで始まっているので、無理だと思うのですが。

Anonymous said...

'_'から始めるとか?
やめて欲しいですけどねw