Я написал 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 застрял на вывод типа возврата. С явным типом возврата все работало нормально. Это ограничение вывода шаблона? Есть ли обходной путь?
Я не верю, что есть способ обойти это. Вы создаете лямбду со следующим определением:
[](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);
}
};
Учитывая этот код, ваш компилятор должен определить тип возвращаемого значения как общий тип двух операторов возврата.
С вашим шаблоном 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 должно быть известно во время компиляции.