由于无限的编译时递归,以下代码的编译存在问题。 Clang 3.6.0 给出了有关递归模板实例化深度的错误,并且不会终止;而 GCC 4.9.2 保持沉默,也不会终止。
这是一个比我最初遇到的例子更简单的例子,bar
的第二个重载当然可以被赋予 int
作为返回类型;在这种情况下,第一个重载被选择用于调用 main
中的 bar
;正如我最初希望的那样。为什么 decltype
的应用程序没有解析到专门用于 Foo
的第一个重载?
Foo
的第二个(默认)模板参数似乎在这里隐藏了关于递归问题的更详细的消息。这在原始上下文中很有用。
template <typename T, typename A = T>
struct Foo {};
template <typename T, typename A>
int bar(const Foo<T,A> &x) { return 0; }
template <typename T>
auto bar(const T &x)
-> decltype(bar(Foo<T>{})){
return bar(Foo<T>{});
}
int main(int argc, char *argv[])
{
Foo<char> f;
bar(f);
return 0;
}
最佳答案
这会失败,因为 decltype
表达式需要为那些参数计算出 bar
的返回类型,所以它需要实例化所有 bar
模板,其中涉及查看 decltype
表达式,该表达式需要为这些参数计算出 bar
的返回类型,因此它需要实例化所有 bar
templates...我相信你可以想象这最终会导致编译器崩溃。
在 C++14 中,我们可以省略尾随返回类型来避免所有这些工作。在 C++11 中,当 T
是 Foo
时,我们可以禁用该重载:
namespace detail {
template <typename T>
struct is_foo : std::false_type{};
template <typename T, typename A>
struct is_foo<Foo<T,A>> : std::true_type{};
}
template <typename T>
using is_foo = detail::is_foo<typename std::decay<T>::type>;
template <typename T, typename A>
int bar(const Foo<T,A> &x) { return 0; }
template <typename T,
typename = typename std::enable_if<!is_foo<T>::value>::type>
auto bar(const T &x)
-> decltype(bar(Foo<T>{})){
Foo<T> b{};
return bar(b);
}
关于c++ - decltype的非终止递归使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32350698/