为什么我需要为传递给 Y 运算器的函数指定返回值

Why do I need to specify a return value for a function I'm passing to a Y combinator

本文关键字:运算器 函数 返回值 为什么      更新时间:2023-10-16

我写了一个这样的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 一样调用它时,它被困在推断返回类型上。使用显式返回类型,一切正常。这是模板扣除的限制吗?有解决方法吗?

我不相信有办法解决这个问题。您可以使用以下定义创建 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 个 return 语句的通用类型。

使用模板 instation,它首先需要知道实例化的返回类型,然后再计算该实例化的答案。

对于这种特定情况,仍然可以正确推断出它。如果在两者之间添加额外的间接寻址并追索回您的类型,会发生什么情况?

类型推导无条件地应用于 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 的值。