2011年12月25日日曜日

【C++11】 Designated Initializer 風な何か シーケンス用

元々、【C++11 Advent Calendar 2011】17日目 小ネタ集 後編用にネタコード作ろうと思ってちょこちょことやっていた訳ですが間に合わなさそうだったので、16進浮動小数点定数やってみたら割とさくっとできたのでそっちだけ記事に書いたわけですが。とりあえずそれっぽいものができたので公開してみます。https://github.com/yak1ex/cpp_stuff の、dinit.hpp, index_tuple.hpp, dinit.cpp になります。とりあえず GCC 4.7 -std=c++0x 限定です。自分が書いてるコードが悪いような気がしますが。後どこかで dangling reference ってる気もします。

#include <vector>
#include <iostream>

#include "dinit.hpp"

int main(void)
{
 using yak::util::dinit::di;
 using yak::util::dinit::idx;
 using yak::util::dinit::idx_;

 std::vector<int> v = di(idx<10>() = 1, idx<0>() = 2, idx<2>() = 3, idx<3>() = 4);
 for(auto val : v) {
  std::cout << val << std::endl;
 }
 std::vector<int> v2 = di(idx_<10>(1), idx_<0>(2), idx_<2>(3), idx_<3>(4));
 for(auto val : v2) {
  std::cout << val << std::endl;
 }
 return 0;
}

出力

2
0
3
4
0
0
0
0
0
0
1
2
0
3
4
0
0
0
0
0
0
1

見ての通り、di() の中で idx<添字>() = 値、あるいは idx_<添字>(値) を並べるとそれに沿って初期化されます。それに沿って初期化されたものによってコピーないしムーブ初期化されます、の方が正確かもしれません。ただ -O2 の段階でも main() 関数内に初期値(2,0,3,4,0,...,0,1)の push が展開されていたのでほぼ初期化相当と見ていいんじゃないでしょうか。↑は vector ですが、initializer-list constructor 持ってるやつ(deque, list, forward_list)なら動いているようです、多分。配列や array にも使えないという残念仕様ですが。また、普通同じ添字のものがあったら最後が有効になると思うでしょうが、最初のものが有効になります。これは直そうと思えばすぐ直せそうですが。

コード的には di() でテンプレート変換演算子を持っているクラスの一時オブジェクトを作成、テンプレート変換演算子中で list initialization している形になります。できればせめて di{} にしたかったのですが早々に諦めました。イメージ的には↓のような感じでこれに実際に実現するためのあれやこれやをつけた感じです。

template<typename Tuple, std::size_t ... idxN>
struct Initer
{
 constexpr Initer(const Tuple& t) : args(t) {}
 template<typename U>
 constexpr operator U()
 {
  return { /* ... */ };
 }
 Tuple args;
};

template<typename ... T>
constexpr const auto di(T&& ... t) -> /* Initer<std::tuple<...>, ...> */;

0 件のコメント:

コメントを投稿