2010-06-07

C++0xの中でいらない機能

私は、C++0xのすべてに賛同しているわけではない。むしろ逆に、不満は多い。オレの欲しかったこの機能がないという不満ではない。こんな機能いらないだろという不満である。

ユーザー定義リテラル

ユーザー定義リテラルは、無駄である。識別子はかならずアンダースコアから始まらなければならない。これは、とても危険だ。なぜ、予約語の制限を無視できるかというと、ユーザー定義リテラルの識別子は、エンティティではない。すなわち、名前ではないからだ。名前ではないゆえに、予約語の制限にはあてはまらない。

ましてや、ユーザー定義リテラルを、名前空間の中に入れることができないというのは、致命的だ。もちろん、名前空間の中でも定義できるが、おそらく、実用にならないだろう。

それに、ユーザー定義リテラルでなければできないことというものがない。では、単なるシンタックスシュガーか。

// ユーザー定義リテラル
auto x = "hogehoge"_hoge ;

// 関数
auto y = hoge("hogehoge") ;

// クラスへのキャスト
auto z = hoge("hogehoge") ;

この砂糖は、果たして既存のクラスやキャストより甘いのであろうか。関数やキャストで十分ではないか。

これに対して、「おいおい江添、バカだなぁ。お前まだ、テンプレートメタプログラミングを知らないのか。ユーザー定義リテラルを使えば、整数の引数が、テンプレート実引数になるじゃないか」という意見がある。それは、承知している。連中の言い分はこうだ。

template < char ... args >
struct placeholder {} ;

template < char ... args >
placeholder< args... > operator "" _ (){ return placeholder< args... >() ; }


int main()
{
    1_ ;// placeholder<'1'>
    2_ ;// placeholder<'2'>
    123_ ;// placeholder<'1', '2', '3'>
}

ご覧のように、リテラル演算子テンプレート(literal operator template)の場合、実引数ではなく、テンプレート実引数に、リテラルの文字列が、一文字づつ渡される。これをつかえば、std::bindに使われる、std::placeholders::_1のような、汚い実装は必要ないではないかということである。

しかし、本当にこんなコードを書きたいだろうか。私は御免だ。このコードを書くには、Variadic Templatesとユーザー定義リテラルに、相当精通していなければならない。そもそもこれは、ユーザー定義リテラル本来の用途ではない。たまたま、こういう事にも使えるというだけだ。メタプログラミングに使いたいのならば、最初からメタプログラミング目的の機能を設計すべきだ。

long long int

long long intは、C99から輸入したものである。C畑の連中の考えることは、たいていクソである。これも例外ではない。long long intは、明確に規定していないものの、limit.hの定義を考えれば、少なくとも64bit以上の整数である。

じゃあ、将来、128bit以上を保証する整数が欲しくなったらどうするのか。long long long intを付け加えるのだろうか。では256bitは如何。子供の考えた必殺技じゃあるまいし、バカバカしい。

初期化リスト

これまで、構造体や配列は、特別な初期化の文法を使うことが出来た。

int x[3] = {1, 2, 3} ;

初期化リストは、これを、ユーザー定義のクラスにも、使えるようにしたものだ。

std::vector<int> x = {1, 2, 3} ;

なるほど、確かに教科書の例文を書く際には、分かりやすい。多分、初心者受けもいいだろう。これは、一見して、良い機能に見える。

しかし、ソースコードにデータを直接記述して初期化するような決め打ちの泥臭い処理が、そんなに頻繁に使われるだろうか。配列や構造体は、コンパイル時に初期化の方法を伝えられるという利点がある。現実のCのコードでも、テーブルの構築などにも使われている。しかし、ユーザー定義のクラスは、そのような簡単な、ビットワイズな初期化をすることはできない。

もちろん、シンタックスシュガーは重要だ。しかし、現実には、ファイルを読み込むだとか入力を受け付けるなどしなければ、初期化ができない場合がある。これは、結局、実行時まで、初期化ができないことを意味する。

もちろん、初期化リストに使えるのは、リテラルだけではない。

void f( int x, int y, int z )
{
    std::vector<int> x = {x, y, z} ;
}

しかし、要素数も、実行時に得られる情報に依存しているならば、結局、初期化リストを使うことはできない。

std::vector<int> v ;
while( int value = get_runtime_input_somehow() )
{
    v.push_back(value) ;
}

とはいっても、初期化リストは、まだそれなりに使い道はあるだろう。それほど便利ではないというだけの話だ。結局、C++の思想である、組み込み型とユーザー定義型を、完全に同じ文法で扱えるべきだという思いが、影響しているものと思われる。

std::bind

不要。lambdaを使うべき。

1 comment:

Anonymous said...

ソースコードは人間が書くものとは
限らないので、ソースコードを
プログラムから自動生成する際に
データを初期化リストに突っ込むのは、
珍しいことではないでしょう

自分は経験ないのですが、
ファイルシステムが
ないような環境で動作させるには、
ソースコードにデータを書くのが簡単なので、
初期化リストは有効なのかも