3

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

4

2 回答 2

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 个返回语句的通用类型。

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

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

于 2018-12-22T09:17:29.927 回答
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 的值必须在编译时知道。

于 2018-12-22T09:49:40.980 回答