【发布时间】:2015-08-30 00:46:53
【问题描述】:
对于一个带有表达式模板的类,我在重载运算符的返回类型推导过程中偶然发现了以下错误。下面的例子说明了错误:
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;
}
当尝试用 g++-5 或 clang++ 编译时,我得到了这个错误
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;
}
诚然,这是一个学术问题,因为解决方法是可能的。但是任何人都可以阐明这种行为是符合标准还是编译器错误?
【问题讨论】:
标签: c++ templates c++11 language-lawyer decltype