http://stackoverflow.com/questions/7381805/c-c11-switch-statement-for-variadic-templates の日本語要約
Q:
DoSomething メンバ関数を持つ複数のクラスを std::tuple に入れた.
整数 i を用いて std::get(t).DoSomething() とすることでタプル t の i 番目の要素の DoSomething() を呼べる.
ここで i は実行時に決まるとすると, 動的ディスパッチするために
switch(i) {
case 0: get<0>(t).DoSomething(); break;
case 1: get<1>(t).DoSomething(); break;
case 2: get<2>(t).DoSomething(); break;
case 3: get<3>(t).DoSomething(); break;
}
としていたんだけど, 長ったらしくて変更に弱い. マクロで case を展開しても限界がある.
C++11 での良いやり方は?
A:
pack展開して関数ポインタテーブル作っちゃえばいいんだよ. 再帰でテンプレート関数探すとswitchより効率悪いしね.
template<int... Indices>
struct indices {
typedef indices<Indices..., sizeof...(Indices)> next;
};
template<int N>
struct build_indices {
typedef typename build_indices<N - 1>::type::next type;
};
template<>
struct build_indices<0> {
typedef indices<> type;
};
// No need to be variadic on the tuple elements as we don't care about them
// So I'm using perfect forwarding for the tuple
template<typename Tuple, int... Indices>
void
do_something_by_index(Tuple&& tuple, int index, indices<Indices...>)
{
using std::get;
typedef void (*element_type)(Tuple&&);
static constexpr element_type table[] = {
[](Tuple&& tuple)
{ get<Indices>(std::forward<Tuple>(tuple)).DoSomething(); }
...
};
table[index](std::forward<Tuple>(tuple));
}
// Proverbial layer of indirection to get the indices
template<typename Tuple>
void
do_something_by_index(Tuple&& tuple, int index)
{
typedef typename std::decay<Tuple>::type decay_type;
constexpr auto tuple_size = std::tuple_size<decay_type>::value;
typedef typename build_indices<tuple_size>::type indices_type;
do_something_by_index(std::forward<Tuple>(tuple), index, indices_type{});
}