呼び出すテンプレート関数を実行時に決定できますか? のような何か:テンプレート関数の動的ディスパッチ?
template<int I>
struct A {
static void foo() {/*...*/}
};
void bar(int i) {
A<i>::f(); // <-- ???
}
呼び出すテンプレート関数を実行時に決定できますか? のような何か:テンプレート関数の動的ディスパッチ?
template<int I>
struct A {
static void foo() {/*...*/}
};
void bar(int i) {
A<i>::f(); // <-- ???
}
テンプレートを扱うときに、コンパイル時と実行時を橋渡しする典型的な 'トリック'は、バリアント型の訪問です。これは、一般的なイメージライブラリ(Boost.GILまたはスタンドアローンとして利用可能)が、例えば実行するものです。それは、典型的には、の形式をとります。
typedef boost::variant<T, U, V> variant_type;
variant_type variant = /* type is picked at runtime */
boost::apply_visitor(visitor(), variant);
visitor
は、単に転送テンプレートへのポリモーフィックファンクタです:
struct visitor: boost::static_visitor<> {
template<typename T>
void
operator()(T const& t) const
{ foo(t); } // the real work is in template<typename T> void foo(T const&);
};
これは、タイプのリストは、そのテンプレートが/ことができます素敵なデザインを持っています(ここでは、variant_type
型シノニム)は、コードの残りの部分に結合されていません。 boost::make_variant_over
のようなメタ機能は、使用するタイプのリストに対する計算も可能にする。
このテクニックは非タイプのパラメータでは使用できないため、手動で訪問をアンロールする必要があります。残念ながら、コードは読み込み可能/保守可能ではありません。
void
bar(int i) {
switch(i) {
case 0: A<0>::f(); break;
case 1: A<1>::f(); break;
case 2: A<2>::f(); break;
default:
// handle
}
}
上記スイッチの繰返しに対処する通常の方法は、(AB)がプリプロセッサを使用することです。より良い
#ifndef LIMIT
#define LIMIT 20 // 'reasonable' default if nothing is supplied at build time
#endif
#define PASTE(rep, n, _) case n: A<n>::f(); break;
void
bar(int i) {
switch(i) {
BOOST_PP_REPEAT(LIMIT, PASTE, _)
default:
// handle
}
}
#undef PASTE
#undef LIMIT
(PASTE
どちらにも損はない)LIMIT
のための良い、自己文書名を見つけ、そしてただ一つのサイトに上記のコード生成を制限:Boost.Preprocessorを使用して(未テスト)の例。 Davidのソリューションとあなたのコメントから
ビル:
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;
};
template<int... Indices>
void
bar(int i, indices<Indices...>)
{
static void (*lookup[])() = { &A<Indices>::f... };
lookup[i]();
}
が、その後
bar
を呼び出す:
N
はあなたの定数時間一定になる
bar(i, typename build_indices<N>::type())
、
sizeof...(something)
。
template<int N>
void
bar(int i)
{ bar(i, typename build_indices<N>::type()); }
bar<N>(i)
と呼ばれている:あなたは、その呼び出しの「醜さ」を隠すためにレイヤーを追加することができます。
テンプレートは、時間の多型は、時間の多型を実行しないでコンパイル実装NO。
いいえ、テンプレートはコンパイル時の機能であり、i
はコンパイル時に認識されないため、これは不可能です。 A<I>::foo()
は、A::foo(i)
のようなものに適合させるべきです。
テンプレート引数はコンパイル時に認識されている必要があります。したがって、コンパイラがA<i>::foo()
を渡す方法はありません。あなたは回避したい場合は
あなたもtemplate
bar()
を行う必要があります。そのために
template<int i>
void bar() {
A<i>::f(); // ok
}
、コンパイル時にbar()
に引数を知っている必要があります。
正確に何をしたいか(つまり、使用するインスタントリミットが少数ですか)に応じて、ルックアップテーブルを作成してそれを動的に使用することができます。完全手動のアプローチのために、オプション0、1、2、および3で、あなたができる:もちろん
void bar(int i) {
static void (*lookup[])(void) = { &A<0>::foo, &A<1>::foo, &A<2>::foo, &A<3>::foo };
lookup[i]();
}
を、私は、例えば、最も単純なオプションを選択しました。必要な番号が連続していない場合やゼロベースの場合は、配列ではなくstd::map<int, void (*)(void) >
を使用することをお勧めします。使用する異なる選択肢の数が多い場合は、手動でテンプレートを入力するのではなく、自動的にテンプレートを強化するコードを追加することもできます。ただし、テンプレートの各インスタンス化によって新しいテンプレートが作成されることを考慮する必要があります。実際にそれが必要かどうかを確認することができます。
EDIT:私はpostと同じ初期化を実装していますが、これはC++ 03の機能のみを使用しているため、答えが長すぎるようです。
Luc Dantonは、とりわけC++ 0x構造体を使ったルックアップテーブルの初期化を含む面白い答えhereを書きました。私はその解決策から、余分な引数を必要とするインタフェースを変更するのはあまり好きではありませんが、中間のディスパッチャで簡単に解決できます。
(コンパイル時に)いくつのケースがあるのか分かっていれば、私のためにコンパイラを展開することはできますか? – Predrag
申し訳ありませんが、痛みを感じていますが、ケースの数についての私の知識は 'size ...()'演算子に由来します。私はプリプロセッサがそのような場合に助けにならないことを恐れています。あなたは、その制約を私に助けてくれる何かを考えることができますか? – Predrag
@Predrag十分な上限を選び、あなたが快適であることを確認します。 –