Bene, il tuo codice non funziona. Ma questo fa:
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>;
Codice di prova:
ycombinator bob = {[x=0](auto&& self)mutable{
std::cout << ++x << "\n";
ycombinator ret = {self};
return ret;
}};
bob()()(); // prints 1 2 3
Il tuo codice è sia UB che mal formato, non è richiesta alcuna diagnostica. Che è divertente; ma entrambi possono essere riparati indipendentemente.
Innanzitutto, 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);
questo è UB perché outer prende selfper valore, quindi inner acquisisce selfper riferimento, quindi procede a restituirlo al outertermine dell'esecuzione. Quindi il segfault è decisamente ok.
La correzione:
[&](auto self) {
return [self,&a](auto b) {
std::cout << (a + b) << std::endl;
return self(self);
};
};
Il codice rimane è mal formato. Per vedere questo possiamo espandere i lambda:
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);
questo istanzia __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;
};
Quindi dobbiamo determinare il tipo di ritorno di __outer_lambda__::operator() .
Lo esaminiamo riga per riga. Per prima cosa creiamo__inner_lambda__ tipo:
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;
};
Ora, guarda lì: il suo tipo di ritorno è self(self) , o __outer_lambda__(__outer_lambda__ const&). Ma siamo nel mezzo del tentativo di dedurre il tipo di restituzione di__outer_lambda__::operator()(__outer_lambda__) .
Non ti è permesso farlo.
Mentre in effetti il tipo di ritorno di __outer_lambda__::operator()(__outer_lambda__) non dipende dal tipo restituito di__inner_lambda__::operator()(int) , C ++ non si preoccupa quando si deducono i tipi restituiti; controlla semplicemente il codice riga per riga.
E self(self) è usato prima che lo deducessimo. Programma mal formato.
Possiamo aggiustarlo nascondendoci self(self)fino a più tardi:
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);
}
e ora il codice è corretto e viene compilato. Ma penso che questo sia un po 'di hack; usa semplicemente ycombinator.