Chà, mã của bạn không hoạt động. Nhưng điều này không:
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>;
Mã kiểm tra:
ycombinator bob = {[x=0](auto&& self)mutable{
std::cout << ++x << "\n";
ycombinator ret = {self};
return ret;
}};
bob()()(); // prints 1 2 3
Mã của bạn là cả UB và không định hình không cần chẩn đoán. Thật là buồn cười; nhưng cả hai có thể được cố định độc lập.
Đầu tiên, UB:
auto it = [&](auto self) { // outer
return [&](auto b) { // inner
std::cout << (a + b) << std::endl;
return self(self);
};
};
it(it)(4)(5)(6);
đây là UB vì bên ngoài lấy self
theo giá trị, sau đó chụp bên trong self
bằng tham chiếu, sau đó tiến hành trả lại sau khi outer
chạy xong. Vì vậy, segfaulting chắc chắn là ok.
Cách khắc phục:
[&](auto self) {
return [self,&a](auto b) {
std::cout << (a + b) << std::endl;
return self(self);
};
};
Các mã vẫn còn hình thành. Để thấy điều này, chúng ta có thể mở rộng 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);
điều này ngay lập tức __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;
};
Vì vậy, tiếp theo chúng ta phải xác định loại trả về __outer_lambda__::operator()
.
Chúng tôi đi qua nó từng dòng. Đầu tiên chúng ta tạo __inner_lambda__
kiểu:
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;
};
Bây giờ, hãy nhìn vào đó - kiểu trả về của nó là self(self)
, hoặc __outer_lambda__(__outer_lambda__ const&)
. Nhưng chúng tôi đang cố gắng suy luận kiểu trả về __outer_lambda__::operator()(__outer_lambda__)
.
Bạn không được phép làm điều đó.
Mặc dù trên thực tế, kiểu trả về __outer_lambda__::operator()(__outer_lambda__)
không thực sự phụ thuộc vào kiểu trả về __inner_lambda__::operator()(int)
, C ++ không quan tâm khi khấu trừ các kiểu trả về; nó chỉ đơn giản là kiểm tra từng dòng mã.
Và self(self)
được sử dụng trước khi chúng tôi suy luận nó. Ill hình thành chương trình.
Chúng ta có thể vá điều này bằng cách ẩn self(self)
cho đến sau này:
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);
}
và bây giờ mã là chính xác và biên dịch. Nhưng tôi nghĩ rằng đây là một chút hack; chỉ cần sử dụng ycombinator.