定点组合子和显式结果类型

时间:2017-09-28 03:28:32

标签: c++ lambda c++14 language-lawyer c++17

要在本地执行一些递归任务,我使用以下方法来创建固定点组合器:

#include <utility>
#include <list>
#include <memory>
#include <iostream>

int main()
{
    struct tree
    {
        int payload;
        std::list< tree > children = {}; // std::list of incomplete type is allowed
    };
    std::size_t indent = 0;
    // indication of result type here is essential
    const auto print = [&] (const auto & self, const tree & node) -> void
    {
        std::cout << std::string(indent, ' ') << node.payload << '\n';
        ++indent;
        for (const tree & t : node.children) {
            self(self, t);
        }
        --indent;
    };
    print(print, {1, {{2, {{8}}}, {3, {{5, {{7}}}, {6}}}, {4}}});
}

工作正常并打印:

1
 2
  8
 3
  5
   7
  6
 4

但是如果我删除显式指定的结果类型-> void,那么我得到编译错误(GCC 8):

  

prog.cc:实例化&#39; main():: [with auto:1 = main()::]&#39;:

     

prog.cc:24:64:从这里要求

     

prog.cc:20:17:错误:使用&#39; main():: [与auto:1 = main()::]&#39;在扣除&#39; auto&#39;

之前
         self(self, t);

(第7条):

  

prog.cc:20:13:错误:函数&#39;运算符()&lt;(lambda at prog.cc:15:24)>&#39;具有推导的返回类型在定义之前不能使用

        self(self, t);

        ^
     

prog.cc:24:10:注意:在实例化函数模板特化&#39; main()::(anonymous class):: operator()&lt;(lambda at prog.cc:15:24) &GT;&#39;请求

print(print, {1, {{2, {{8}}}, {3, {{5, {{7}}}, {6}}}, {4}}});

     ^
     

prog.cc:15:24:注意:&#39; operator()&lt;(lambda at prog.cc:15:24)>'在这里宣布

const auto print = [&] (const auto & self, const tree & node)

                   ^
     

生成了1个错误。

错误的原因是什么?我认为编译器可以推断出看函数体的结果类型。结果类型不依赖于&#34;模板&#34; self参数类型。

2 个答案:

答案 0 :(得分:3)

为了推导返回类型,lambda(更好,它的调用运算符)被实例化,并且需要完全定义,主要是因为返回类型是从任何非丢弃的return语句推导出来的。当你在体内使用它时,由于显而易见的原因它尚未完全定义,因此返回类型仍然是未知的。因此,不可能说出表达的类型是什么,程序是不正确的。

答案 1 :(得分:2)

[dcl.spec.auto]中的规则是:

  

如果需要具有未减弱占位符类型的实体类型来确定表达式的类型,则程序格式错误。但是,一旦在函数中看到了一个非丢弃的return语句,那么从该语句推导出的返回类型可以在函数的其余部分中使用,包括在其他return语句中。

如果您明确指定print(print, {1, {{2, {{8}}}, {3, {{5, {{7}}}, {6}}}, {4}}}); 作为返回类型,则没有未受影响的占位符类型,因此我们很好。

但如果我们不这样做,那么当我们调用

self(self, t)

在表达式print中,需要operator() {{1}}的类型(#34;具有未减少占位符类型的实体&#34;)才能确定表达式的类型,所以我们与第一句话发生冲突。