我正在爲由任意數量的char
標籤進行參數化的表達式編寫模板。英特爾C++編譯器編譯速度極慢遞歸decltype返回
給定一個參數列表,一個工廠函數根據是否有兩個相同類型的參數或者它們是否唯一,返回不同類型的表達式。
一個具體的例子:假設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'>
。
要做到這一點,我不得不與auto
和decltype
定義?Expression
的工廠函數。此外,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秒才能編譯。對於五個參數,編譯時間可以擴展到大約0.29秒,對於十個參數,編譯時間可以擴展到0.62秒。這完全合理。
這個故事與英特爾編譯器有很大不同。 ICPC 13.0.1在0.35秒內編譯單參數代碼,編譯時間對於最多四個參數保持不變。在五個參數中,編譯時間長達12秒,而在六個參數上,它在9600秒以上(即超過2小時40分鐘)上升。不用說,我沒有等待足夠長的時間來找出編譯七個參數版本需要多長時間。
兩個問題立刻浮現在腦海:
是英特爾編譯器尤其已知慢編譯遞歸
decltype
?有沒有什麼辦法來重寫這段代碼,以達到相同的效果,或許對編譯器更友好?
這是旁白:http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-ac-identifier不要使用以下劃線開頭的符號你的代碼。標準庫可以,但你不應該。 – Yakk
do_build是你唯一關心的類型嗎?如果是這樣,請嘗試返回'struct SameType {template operator R()const {return R(); }};' - 如果編譯它會削減大量的複製粘貼樣板,並且可能是編譯時的指數加速。 –
Yakk
在英特爾支持論壇上也發佈了這個問題,附近有很多非常有見識的人。 –