【问题标题】:Why do I need to specify a return value for a function I'm passing to a Y combinator为什么我需要为传递给 Y 组合器的函数指定返回值
【发布时间】:2019-05-22 11:40:36
【问题描述】:

我写了一个这样的 Y 组合器:

template <class F>
struct Y{
  F f;
  Y(F _f) : f{_f} {}
  template<class...arg_t>
  auto operator()(arg_t&&...arg) {return f(*this,std::forward<arg_t>(arg)...);}
};

它有效,但是当我尝试定义阶乘时

auto fact = Y{[](auto&& self, int n) {
                if (n<=1) return 1;
                return n*self(n-1);}};

它会编译,但是当我像f(3) 那样调用它时,clang 被困在推断返回类型上。使用显式返回类型,一切正常。这是模板扣除的限制吗?有解决办法吗?

【问题讨论】:

  • 类型推导也需要定点。但它无条件地适用于两个 return 语句。

标签: c++ templates functional-programming template-argument-deduction y-combinator


【解决方案1】:

类型推导无条件地应用于 Y 组合器的两个返回语句,因为变量 n 保存的信息不是常量表达式(编译器在编译时知道的表达式)。所以通过类型推导找不到不动点。

如果n的值在编译时已知,则类型推断成功,例如:

struct fact_overloads{
  template<class Self,int n>
  constexpr auto 
  operator()(Self&& self, std::integral_constant<n>){
    if constexpr (n<=1) return 1;
    else return n * self(std::integral_constant<n-1>{});
    };
  };

auto fact = Y{fact_overloads{}};

但是这样一个函数的用例集是有限的,因为 n 的值必须在编译时知道。

【讨论】:

    【解决方案2】:

    我不相信有办法解决它。您创建一个具有以下定义的 lambda:

    [](auto&& self, int n) {
                if (n<=1) return 1;
                return n*self(n-1);
     }
    

    这转化为:

    struct lambda
     {
      template <typename T1>
      constexpr auto operator()(T1&&self, int n) const
       {
                if (n<=1)
                      return 1;
                return n*self(n-1);
        }
    };
    

    鉴于该代码,您的编译器应将返回类型推断为 2 个返回语句的通用类型。

    对于您的模板实例,它首先需要知道实例化的返回类型,然后才能计算该实例化的答案。

    对于这种特殊情况,可能仍然可以正确推断。如果您在两者之间添加额外的间接寻址并重新使用您的类型,会发生什么情况?

    【讨论】:

    • 我有一个类似的想法,并在对 Godbolt 进行了一番摆弄之后找到了您的答案。我使用g++ -std=c++17 从替代编译器获得“第二意见”。这就是我所做的:Test on godbolt。有趣的是,它的行为与@Riley 所描述的一样。只要您注释掉调用fact(3),它就会成功编译,甚至会为auto fact 创建一些代码,尽管我不明白这有什么好处,也不知道它是否有任何用处。
    • 正如它所写的那样,您的答案可能会令人困惑。编译器必须检查所有返回语句表达式推导的类型是否相同dcl.spec.auto普通类型这个词有特殊的含义(大约是条件表达式的衰减类型)。
    猜你喜欢
    • 2017-01-03
    • 1970-01-01
    • 2018-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-14
    • 2013-12-29
    • 2021-05-23
    相关资源
    最近更新 更多