任意の数のchar
ラベルでパラメーター化された式のテンプレートを作成しています。
引数リストを指定すると、ファクトリ関数は、同じ型の 2 つの引数があるかどうか、またはそれらが一意であるかどうかに応じて、異なる型の式を返します。
具体的な例:を生成するためA
にオーバーロードされた「ラベル付け可能な」オブジェクトであるとします。ラベルとして宣言してみましょう。次に、を生成しますが、代わりに を生成します。operator()
?Expression<...>
a, b, ...
LabelName<'a'>, LabelName<'b'>, ...
A(a,b,c,d)
UniqueExpression<'a','b','c','d'>
A(a,c,b,c)
RepeatedExpression<'a','c','b','c'>
?Expression
これを実現するには、のファクトリー関数をauto
とで定義する必要がありdecltype
ました。さらに、メタプログラムが引数の再帰を終了し、戻り値の型が最終的に決定されるまで、decltype
他のものにカスケードする必要があります。decltype
実例として、ファクトリ メソッドのかなり最小限のコードを分離しました。
template <typename... T> struct TypeList { };
template <char C> struct LabelName { };
template <typename... T> class UniqueExpression
{
// Contains implementation details in actual code
};
template <typename... T> class RepeatedExpression
{
// Contains implementation details in actual code
};
class ExpressionFactory {
private:
template <char _C, typename... T, typename... _T>
static UniqueExpression<T...>
_do_build(TypeList<T...>,
TypeList<LabelName<_C>>,
TypeList<>,
TypeList<_T...>)
{
return UniqueExpression<T...> ();
}
template <char _C, typename... T, typename... _T1, typename... _T2, typename... _T3>
static RepeatedExpression<T...>
_do_build(TypeList<T...>,
TypeList<LabelName<_C>, _T1...>,
TypeList<LabelName<_C>, _T2...>,
TypeList<_T3...>)
{
return RepeatedExpression<T...> ();
}
template <char _C1, char _C2, typename... T, typename... _T1, typename... _T2, typename... _T3>
static auto
_do_build(TypeList<T...>,
TypeList<LabelName<_C1>, _T1...>,
TypeList<LabelName<_C2>, _T2...>,
TypeList<_T3...>)
-> decltype(_do_build(TypeList<T...>(),
TypeList<LabelName<_C1>, _T1...>(),
TypeList<_T2...>(),
TypeList<_T3..., LabelName<_C2>>()))
{
return _do_build(TypeList<T...>(),
TypeList<LabelName<_C1>, _T1...>(),
TypeList<_T2...>(),
TypeList<_T3..., LabelName<_C2>>());
}
template <char _C1, char _C2, typename... T, typename... _T1, typename... _T2>
static auto
_do_build(TypeList<T...>,
TypeList<LabelName<_C1>, LabelName<_C2>, _T1...>,
TypeList<>,
TypeList<LabelName<_C2>, _T2...>)
-> decltype(_do_build(TypeList<T...>(),
TypeList<LabelName<_C2>, _T1...>(),
TypeList<_T2...>(),
TypeList<>()))
{
return _do_build(TypeList<T...>(),
TypeList<LabelName<_C2>, _T1...>(),
TypeList<_T2...>(),
TypeList<>());
}
public:
template <char C, typename... T>
static auto
build_expression(LabelName<C>, T...)
-> decltype(_do_build(TypeList<LabelName<C>, T...>(),
TypeList<LabelName<C>, T...>(),
TypeList<T...>(),
TypeList<>()))
{
return _do_build(TypeList<LabelName<C>, T...>(),
TypeList<LabelName<C>, T...>(),
TypeList<T...>(),
TypeList<>());
}
};
ファクトリは次のようにプログラムで呼び出すことができます: (実際のプログラムではoperator()
、ファクトリを呼び出すオーバーロードされた別のクラスがあります)
int main()
{
LabelName<'a'> a;
LabelName<'b'> b;
...
LabelName<'j'> j;
auto expr = ExpressionFactory::build_expression(a,b,c,d,e,f,g,h,i,j);
// Perhaps do some cool stuff with expr
return 0;
}
上記のコードは意図したとおりに機能し、GCC と Intel コンパイラの両方で正しくコンパイルされます。ここで、使用するラベルの数を増やすと、コンパイラが再帰的なテンプレート推定を実行するのにより多くの時間がかかることがわかりました。
私のコンピューターでbuild_expression
は、 が 1 つの引数で呼び出された場合、GCC 4.7.1 のコンパイルには平均で約 0.26 秒かかります。コンパイル時間は、引数が 5 つの場合は約 0.29 秒、引数が 10 の場合は 0.62 秒になります。これはすべて完全に合理的です。
Intel コンパイラの場合は話が大きく異なります。ICPC 13.0.1 は 1 引数のコードを 0.35 秒でコンパイルし、コンパイル時間は最大 4 つの引数までほぼ一定です。引数が 5 つの場合、コンパイル時間は最大 12 秒になり、引数が 6 つの場合は、9600 秒を超えます (つまり、2 時間 40 分以上)。言うまでもなく、私は 7 引数バージョンのコンパイルにかかる時間を知るのに十分な時間待ちませんでした。
すぐに次の 2 つの質問が思い浮かびます。
Intel コンパイラは、特に再帰的なコンパイルが遅いことが知られています
decltype
か?おそらくコンパイラにとってより使いやすい方法で同じ効果を達成するために、このコードを書き直す方法はありますか?