Eh bien, votre code ne fonctionne pas. Mais cela fait:
template<class F>
struct ycombinator {
F f;
template<class...Args>
auto operator()(Args&&...args){
return f(f, std::forward<Args>(args)...);
}
};
template<class F>
ycombinator(F) -> ycombinator<F>;
Code de test:
ycombinator bob = {[x=0](auto&& self)mutable{
std::cout << ++x << "\n";
ycombinator ret = {self};
return ret;
}};
bob()()(); // prints 1 2 3
Votre code est à la fois UB et mal formé, aucun diagnostic n'est requis. Ce qui est drôle; mais les deux peuvent être fixés indépendamment.
Tout d'abord, l'UB:
auto it = [&](auto self) { // outer
return [&](auto b) { // inner
std::cout << (a + b) << std::endl;
return self(self);
};
};
it(it)(4)(5)(6);
c'est UB parce que externe prend self
par valeur, puis interne capture self
par référence, puis continue à le renvoyer une fois l' outer
exécution terminée. Donc, le segfaulting est définitivement ok.
Le correctif:
[&](auto self) {
return [self,&a](auto b) {
std::cout << (a + b) << std::endl;
return self(self);
};
};
Le code reste mal formé. Pour voir cela, nous pouvons développer les lambdas:
struct __outer_lambda__ {
template<class T>
auto operator()(T self) const {
struct __inner_lambda__ {
template<class B>
auto operator()(B b) const {
std::cout << (a + b) << std::endl;
return self(self);
}
int& a;
T self;
};
return __inner_lambda__{a, self};
}
int& a;
};
__outer_lambda__ it{a};
it(it);
cela instancie __outer_lambda__::operator()<__outer_lambda__>
:
template<>
auto __outer_lambda__::operator()(__outer_lambda__ self) const {
struct __inner_lambda__ {
template<class B>
auto operator()(B b) const {
std::cout << (a + b) << std::endl;
return self(self);
}
int& a;
__outer_lambda__ self;
};
return __inner_lambda__{a, self};
}
int& a;
};
Nous devons donc ensuite déterminer le type de retour de __outer_lambda__::operator()
.
Nous le parcourons ligne par ligne. Nous créons d'abord le __inner_lambda__
type:
struct __inner_lambda__ {
template<class B>
auto operator()(B b) const {
std::cout << (a + b) << std::endl;
return self(self);
}
int& a;
__outer_lambda__ self;
};
Maintenant, regardez là - son type de retour est self(self)
, ou __outer_lambda__(__outer_lambda__ const&)
. Mais nous sommes en train d'essayer de déduire le type de retour de __outer_lambda__::operator()(__outer_lambda__)
.
Vous n'êtes pas autorisé à faire ça.
Alors qu'en fait le type de retour de __outer_lambda__::operator()(__outer_lambda__)
ne dépend pas réellement du type de retour de __inner_lambda__::operator()(int)
, C ++ ne se soucie pas de la déduction des types de retour; il vérifie simplement le code ligne par ligne.
Et self(self)
est utilisé avant de le déduire. Programme mal formé.
Nous pouvons corriger cela en cachant self(self)
à plus tard:
template<class A, class B>
struct second_type_helper { using result=B; };
template<class A, class B>
using second_type = typename second_type_helper<A,B>::result;
int main(int argc, char* argv[]) {
int a = 5;
auto it = [&](auto self) {
return [self,&a](auto b) {
std::cout << (a + b) << std::endl;
return self(second_type<decltype(b), decltype(self)&>(self) );
};
};
it(it)(4)(6)(42)(77)(999);
}
et maintenant le code est correct et compile. Mais je pense que c'est un peu de hack; utilisez simplement le ycombinator.