首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >递归尾随返回类型的名称解析

递归尾随返回类型的名称解析
EN

Stack Overflow用户
提问于 2014-06-04 11:36:26
回答 1查看 2K关注 0票数 24

我发现显式返回类型和自动跟踪返回类型之间有一个奇怪的区别。

在下面的代码中,我们在一个整数和一个iter函数上定义了一个结构模板,它使用一个这种类型的对象作为参数。返回类型取决于模板值递减后调用自身的结果。

为了打破实例化循环(至少我是这么想的),我提供了一个专门化,它返回一个非依赖类型。

我们有一个toy来实例化模板。

下面是一些代码:

代码语言:javascript
复制
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中都不起作用。这两种方法都触发无限实例化(它们与专用的大小写不匹配)。

代码语言:javascript
复制
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)并为模板提供一个主体,它将返回完全相同的内容:

代码语言:javascript
复制
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;
}

现在,这两种方法都适用于编译器,并按预期的方式运行。

我试着用不同的方法来表达专业化,并把它移动了一点(注意它的位置),但这并不能阻止它的自焚;

我还试图在代码中添加更多的decltypedeclval,但似乎无法使C++11语法正常工作。

有人能解释一下名称查找的两个语法之间的区别吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 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>{})的任何尝试调用

代码语言:javascript
复制
template<int i> constexpr auto iter(Int<i>)
  -> decltype(iter(typename std::enable_if<i != 0, Int<i-1>>::type{}));
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        ^^^^^^^

请注意,SFINAE必须在decltype内部,实际上在对iter的调用中。

票数 20
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24036357

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档