我正在为由任意数量的char
标签参数化的表达式编写模板。
给定一个参数列表,工厂函数根据是否有两个相同类型的参数或它们是否唯一来返回不同类型的表达式。
一个具体的例子:假设这A
是一个“可标记”的对象,其operator()
重载以产生一个?Expression<...>
. 让我们a, b, ...
声明为标签LabelName<'a'>, LabelName<'b'>, ...
。ThenA(a,b,c,d)
会产生 a UniqueExpression<'a','b','c','d'>
,而A(a,c,b,c)
会产生 a 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
用一个参数调用,那么 GCC 4.7.1 平均需要大约 0.26 秒来编译。5 个参数的编译时间可延长至 0.29 秒左右,10 个参数的编译时间可延长至 0.62 秒。这都是完全合理的。
英特尔编译器的情况完全不同。ICPC 13.0.1 在 0.35 秒内编译单参数代码,编译时间对于最多四个参数几乎保持不变。在 5 个参数时,编译时间达到 12 秒,在 6 个参数时,编译时间超过 9600 秒(即超过 2 小时 40 分钟)。不用说,我还没有等待足够长的时间来找出编译七参数版本需要多长时间。
两个问题立刻浮现在脑海:
英特尔编译器是否特别以递归编译速度慢而著称
decltype
?有没有办法以对编译器更友好的方式重写此代码以达到相同的效果?