typedefだらけのstring界隈

インターネットニュースを見ていたのだがえらいことだな。
10号地その1にある展示場がえらいことだ。しかしなんやかんや言ってやってしまうんだからすごい。
そんな中今日は、std::string、もといstd::basic_stringの説明書きをカリカリ書いていたがまぁ長い。
いろんな方法で文字列を指定できるようになってるからだよなぁ。
実はまだ完成していない。しかし迷いやすいところではあるので完成させないとなぁ。


STLとか見てるとtypedefだらけ。
typedefにも役に立ってるtypedefとようわからないtypedefがある。
ここではMicrosoft Visual StudioのSTLを見て考えているが、他のSTLではどうなんだろ。
まぁ少なくともどこでもあるだろうtypedefはstd::stringとstd::wstringに関わるもの。

typedef basic_string<char, char_traits<char>, allocator<char> >  string;
typedef basic_string<wchar_t, char_traits<wchar_t>,allocator<wchar_t> > wstring;

実はstd::stringはstd::basic_string<char, char_traits<char>, allocator<char> >なんです。
なんでこんな小細工をするのかというとワイド文字列用のstd::wstringと共通にするため。
これによって2つの文字列クラスを別々に作らなくて済む。
だからといっていちいちstd::basic_stringとか書くのもめんどくさい。
そこでtypedefという手法を使っている。まぁ悪くない例です。
ちなみにstd::basic_stringはSTLには含まれないという解釈が一般的なようです。
STLはコンテナ・イテレータ・アルゴリズム・関数オブジェクトからなるものであると考えられているようです。
そうなるとstd::basic_stringもコンテナとして含まれてもよさそうなものだがちょっと違うようです。
もっともSTLのコンテナと互換性はあるのでSTLと同様に使えると言えそうです。


同様に役に立つtypedefにはreverse_iteratorとかありますね。

typedef std::reverse_iterator reverse_iterator
typedef std::reverse_iterator const_reverse_iterator;

これは大概のコンテナにこの記述があります。
実はリバースイテレータというのは基本のイテレータをstd::reverse_iteratorまたはstd::reverse_bidirectional_iteratorにかぶせて使います。
これにより++すると前にすすむとか、内部的に指している要素の1つ前をさすとかという特徴が現れると。
だからといっていちいちstd::reverse_iterator::iterator>と書くのは骨が折れる。
なのでstd::vector::iteratorと同じようにstd::vector::reverse_iteratorで済むようにしましょうと。
ありがたいことです。
しかし意味不明なこともあって、std::vector::iteratorはtypedefで定義されています。

typedef _Vector_iterator<_Ty, _Alloc> iterator;
typedef _Vector_const_iterator<_Ty, _Alloc> const_iterator;

なぜか、直接クラス定義せずにtypedefしている。
奇妙ですね。まぁ_Vector_iteratorという名前からして表には出ない名前なんでしょうけど。
しかしIntelliSenseの表示にはこの名前とかが出てくるので困る。


IntelliSenseに悩まされることは多いです。
std::string::nposの型名はconst std::basic_string<char, char_traits<char>, allocator<char> >::size_typeだと表示される。
実はこれはconst size_tです。もっというと多分const unsigned intです。
じゃあなんでそう表示しないのかというとそれには複雑な事情があります。
std::basic_string<_Elem,_Traits,_Ax>のsize_typeというのはtypedefで定められていて、

typedef typename _Alloc::size_type size_type;

とある。アロケータによって決まるんですよ。
とはいえstd::allocator<char>を使うのが普通ですからねぇ。
その場合どうなるのかというとヘッダファイル、xmemoryを読みますと、std::allocator<_Ty>においてこうtypedefされている。

typedef _SIZT size_type;

_SIZTってなんだってこの先頭でsize_tと決められている。
普通のメモリではsize_tだということになるわけだ。
size_tというのはCで定められている大きさを表す型で、具体的にはstddef.hで定められている。実際はcrtdefs.hに書いてある。

#ifdef  _WIN64
typedef unsigned __int64 size_t;
#else
typedef _W64 unsigned int size_t;
#endif

64ビット版Windowsではunsigned __int64なのでunsigned intではありません。が、そうでなければunsigned intですね。
まぁとはいえここまで開くと互換性で問題が起きるのでsize_tにとどめるのが普通。


こんなのばっかりです。
なんでこんなばっかりtypedef使うんだって思えてきます。
そりゃ便利なtypedefも多いんですがここまでくるとどうかしている。
Java・C#ではtypedefがないけど、同じものが2つの名前を持つのは複雑だというのが理由らしい。
まぁその主張には一理あるわな。
そもそもC++のテンプレートは濫用しすぎで、理解が難しい。
ダックタイピングに頼りすぎてへんてこなエラーで読みにくかったりね。
std::stringをstd::wstringを共通の部分は共通で用意しようという試みはすばらしいと思うが、最後にtypedefはないと思う。

class string : public basic_string<char, char_traits<char>, allocator<char> >{};
class wstring : public basic_string<wchar_t, char_traits<wchar_t>,allocator<wchar_t> >{};

こんな風に最後に継承すればいいんじゃないの。多分コンストラクタは各自書く必要があるだろうが。
こうやって用意したものをコンパイルして蓄えておけば高速にコンパイルできそう。
まぁ実のところどうなのか知らんが扱いにくいなぁとは思うところだ。