我发现显式返回类型和自动跟踪返回类型之间有一个奇怪的区别。
在下面的代码中,我们在一个整数和一个iter函数上定义了一个结构模板,它使用一个这种类型的对象作为参数。返回类型取决于模板值递减后调用自身的结果。
为了打破实例化循环(至少我是这么想的),我提供了一个专门化,它返回一个非依赖类型。
我们有一个toy来实例化模板。
下面是一些代码:
template<int i> struct Int {};
constexpr auto iter(Int<0>) -> Int<0>;
template<int i> constexpr auto iter(Int<i>) -> decltype(iter(Int<i-1>{}));
int main(){
decltype(iter(Int<10>{})) a;
}这个代码在gcc 4.9和clang 3.5中都不起作用。这两种方法都触发无限实例化(它们与专用的大小写不匹配)。
rec.cpp:11:62: fatal error: recursive template instantiation exceeded maximum depth of 256
template<int i> constexpr auto iter(Int<i>) -> decltype(iter(Int<i-1>{}));现在,如果我们使用C++14 decltype(auto)并为模板提供一个主体,它将返回完全相同的内容:
template<int i> struct Int {};
constexpr auto iter(Int<0>) -> Int<0>;
template<int i>
constexpr auto iter(Int<i>) -> decltype(auto) {
return iter(Int<i-1>{});
}
int main(){
decltype(iter(Int<10>{})) a;
}现在,这两种方法都适用于编译器,并按预期的方式运行。
我试着用不同的方法来表达专业化,并把它移动了一点(注意它的位置),但这并不能阻止它的自焚;
我还试图在代码中添加更多的decltype和declval,但似乎无法使C++11语法正常工作。
有人能解释一下名称查找的两个语法之间的区别吗?
发布于 2014-06-04 17:17:34
这是因为重载解析、模板重载解析、模板声明实例化和模板定义实例化的相对顺序。
让我们先看看C++11的情况。当编译器需要计算decltype(iter(Int<0>{}))时,它会对使用参数prvalue Int<0>调用的名称iter执行重载解析。由于模板位于重载集中,所以我们应用14.8.3 temp.over
函数模板可以通过名称的(非模板)函数或同名的(其他)函数模板重载。当写入对该名称的调用(显式或隐式使用运算符符号)时,对每个函数模板执行模板参数演绎(14.8.2)和检查任何显式模板参数(14.3),以查找可与该函数模板一起使用的模板参数值(如果有的话),以实例化可以使用调用参数调用的函数模板专门化。..。
因此,声明template<int i> constexpr auto iter(...) -> ...被用i = 0实例化(14.7.1p10 temp.inst),这迫使我们对decltype(iter(Int<-1>{}))进行求值,并从负整数的兔子洞中消失。
重要的是,over.match.best),会是一个更好的重载(增加13.3.3p1的constexpr auto iter(Int<0>) -> Int<0>,因为我们从来没有达到这个程度;编译器正欢快地走向负无穷大)。
相反,通过推导返回类型7.1.6.4p12,dcl.spec.auto适用于:
12 -当定义被实例化时,将在其声明类型中带有占位符的函数模板返回类型扣减。
由于定义实例化发生在模板重载解析(14.7.1p3)之后,因此错误的模板iter<0>从未被实例化;14.8.3p5:
5-只需要函数模板专门化的签名才能在一组候选函数中输入专门化。因此,只需要函数模板声明就可以解析模板专门化是候选调用的调用。
这里iter<0>的“签名”是(Int<0>) -> decltype(auto),一个包含占位符类型(7.1.6.4)的签名。
建议的解决办法:使用SFINAE防止对iter(Int<-1>{})的任何尝试调用
template<int i> constexpr auto iter(Int<i>)
-> decltype(iter(typename std::enable_if<i != 0, Int<i-1>>::type{}));
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^请注意,SFINAE必须在decltype内部,实际上在对iter的调用中。
https://stackoverflow.com/questions/24036357
复制相似问题