對於具有表達式模板的類,在重載運算符的返回類型演繹期間,我偶然發現了以下錯誤。下面的例子說明了錯誤:decltype,重載運算符的遞歸類型演繹
template < typename T >
struct A_Number {
T x;
};
// TAG1
template < typename T >
A_Number<T>
operator+(const A_Number<T> &a, const A_Number<T> &b)
{
return {a.x + b.x};
}
// TAG2
template < typename T, typename S >
A_Number<T>
operator+(const A_Number<T> &a, const S &b)
{
return {a.x + b};
}
// TAG3
template < typename T, typename S >
auto
operator+(const S &b, const A_Number<T> &a) -> decltype(a + b)
// ^^^^^
{
return a + b;
}
int
main(void)
{
auto x1 = A_Number<int>{1};
auto x2 = A_Number<int>{1};
auto res1 = x1 + 1; // instantiates TAG2
auto res2 = 1 + x1; // instantiates TAG3, TAG2
auto res3 = x1 + x2; // error, tries to match TAG3
return EXIT_SUCCESS;
}
當試圖相剋編譯此++ - 5或鐺++,我得到這個錯誤
fatal error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum)
operator+(const S &b, const A_Number<T> &a) -> decltype(a + b)
顯然,編譯器嘗試匹配版本TAG3,雖然有更好的匹配(TAG1)可用。當試圖匹配時,他們試圖推導出似乎導致TAG3遞歸實例化的返回類型。爲什麼返回類型扣除沒有看到其他(更好的匹配)重載?即使另一個重載的模板函數具有更好的匹配簽名,推導返回類型是否正確?
有趣的是,這個錯誤在稀薄的空氣中蒸發,省略當返回類型完全與c++14
編譯,就像這樣:
// TAG3
template < typename T, typename S >
auto
operator+(const S &b, const A_Number<T> &a) // C++14
{
return a + b;
}
誠然,這是一個學術問題,因爲解決方法是可能的。但任何人都可以闡明這種行爲是標準符合還是編譯器錯誤?