initializer_listもどきをc++03で実装してみる
c++0xにはinitializer_listというPOD構造体や配列の初期化に使うような記述、
int ar[] = { 1, 2, 3, 4, 5, 6 };
と言うような表記で初期化することができます。
ですが、同じく配列のようにつかうstd::vectorなどはそういう初期化が出来ません。
現在策定中のc++0xでは規格が拡張され、配列の初期化の用な感じでコレクションクラスや普通のクラスなどの初期化が行えるようになります。
でも現行のc++03では出来ないので仕方なくちまちまとstd::copyしたりpush_backしたりするコードができあがります・・・
自分の精神衛生的にあまりよろしくなかったので少しあがいてみました。
まずはコード。
#include <iostream> #include <algorithm> template <typename value_type, int array_length> struct array { typedef value_type element_type[array_length]; element_type elements; typedef value_type* iterator; typedef const value_type* const_iterator; int length() const { return array_length; } int size() const { return length(); } iterator begin() { return &elements[0]; } const_iterator begin() const { return &elements[0]; } iterator end() { return &elements[0] + length(); } const_iterator end() const { return &elements[0] + length(); } value_type& operator[](int offset) { return elements[offset]; } const value_type& operator[](int offset) const { return elements[offset]; } value_type& front() { return elements[0]; } const value_type& front() const { return elements[0]; } value_type& last() { return elements[length() - 1]; } const value_type& last() const { return elements[length() - 1]; } array(): elements() {} array(const array& source) { std::copy(source.begin(), source.end(), this->begin()); } template <int other_array_length> array(const array<value_type, other_array_length>& source, const value_type& last = value_type()): elements() { std::copy(source.begin(), source.end(), this->begin()); this->last() = last; } template <int other_array_length> array(const value_type& first, const array<value_type, other_array_length>& source): elements() { this->front() = first; std::copy(source.begin(), source.end(), this->begin() + 1); } }; template <typename value_type, int array_length = 0> struct initialize_list { typedef array<value_type, array_length> array_type; typedef array<value_type, array_length+1> next_array_type; typedef typename array_type::iterator iterator; typedef typename array_type::const_iterator const_iterator; array_type arguments; initialize_list(): arguments() {} initialize_list(const initialize_list<value_type, array_length-1>& head, const value_type& last): arguments(head.get_arguments(), last) {} array_type get_arguments() const { return arguments; } const_iterator begin() const { return arguments.begin(); } const_iterator end() const { return arguments.end(); } initialize_list<value_type, array_length+1> operator,(const value_type& value) const { return initialize_list<value_type, array_length+1>(*this, value); } }; struct output { public: void operator()(const int& value) const { std::cout << value << std::endl; } }; class test_class { public: template <int length> test_class(const initialize_list<int, length>& init_list) { std::for_each(init_list.begin(), init_list.end(), output()); } }; void test_main() { test_class tester = (initialize_list<int>(), 1, 2, 3, 4, 5); } int main() { test_main(); return 0; }
initialize_listクラスのインスタンスで中に固定長配列を持っていてこれに初期化配列をつんでいく形になります。
で、initialize_list::operator, をオーバーロードしておいてその中で中の固定長配列が一つ分長いinitialize_listを作ってそれに以前の値をコピー、新しい値もケツにつんでは返す、を繰り返すことで実現しています。
arrayクラスはC++ nativeの配列かあまりにふがいないので多少の楽のためにラッピングしてるクラスです。こういう工夫すらないと戻り値で配列を返すことすら出来ないので・・・
compile time展開してくれるかなー?と期待してみたんですがダメでした。initialize_listが拡張していくところで見事に初期化とコピーのコードが入っていました@g++ 4.3.3 on ubuntu 9.04
右辺値代入とかあれば多少はマシになるのかもしれませんが右辺値代入がちゃんと使えるコンパイラを持ってくるとふつうにinitializer_listも使えるだろうしなぁ・・・
うーん、値保持用の配列をtemplateベースのlist構造つかえばもうちょいマシになるんだろうか・・・