2011-10-22 53 views
18

thể trùng lặp:
Recursive lambda functions in c++0xgọi đệ quy trong lambda (C++ 11)

Tại sao tôi không thể gọi một lambda đệ quy nếu tôi viết nó như:

auto a = [&] 
{ 
    static int i = 0; i++; 
    std::cout << i << std::endl; 
    if (i<10) 
    a(); //recursive call 
}; 

Nó cung cấp lỗi biên dịch (ideone):

prog.cpp:8:18: error: '((const main()::<lambda()>*)this)->main()::<lambda()>::a' cannot be used as a function 

prog.cpp: In function 'int main()': 
prog.cpp:9:9: error: variable 'auto a' with 'auto' type used in its own initializer 

Lỗi này có nghĩa là gì?

tôi hiểu được lý do tại sao tôi không thể viết này:

auto i=i+1; //error: unable to deduce 'auto' from '<expression error>' 

Chúng ta không thể viết những dòng này vì kiểu i phải được rút ra từ đó là khởi tạo, có nghĩa là loại không thể được suy luận nếu i chính nó xuất hiện trong khởi tạo (ideone). Nhưng làm thế nào nó quan trọng trong trường hợp lambda? Nếu tôi không sai, kiểu lambda được xác định bởi tham số của nó và kiểu trả về; nó không phụ thuộc vào cơ thể nếu nó không trả về gì (trong trường hợp này, kiểu trả về được suy ra là void, bất kể các câu lệnh khác trong thân lambda).

Dù sao, tôi có một cách giải quyết, và tôi có thể sử dụng thay vì std::function như:

std::function<void()> a = [&] 
{ 
    static int i = 0; i++; 
    std::cout << i << std::endl; 
    if (i<10) 
     a(); 
}; 

mà biên dịch tiền phạt (ideone). Nhưng tôi vẫn muốn biết lý do tại sao phiên bản auto không biên dịch.

+0

Tôi nghĩ rằng vấn đề có liên quan đến bạn kết thúc với một nắm bắt ngầm của 'a', như một tham chiếu sẽ recurse vô hạn vì' a' chính nó kết thúc như là một thành viên của 'a'. – Flexo

+0

@awoodland: Điều đó có nghĩa là gì? Xin hãy giải thích về điều đó. – Nawaz

+3

Loại lambda cũng phụ thuộc vào ảnh chụp, mà tôi nghi ngờ là vấn đề ở đây. Nếu bạn không sử dụng 'a' thì trình biên dịch không quan tâm, nhưng khi bạn thực hiện nó không biết loại đối tượng nào cần sử dụng. –

Trả lời

15

Lý do là không có trường hợp đặc biệt nào cho trình khởi tạo biểu thức lambda của các biến số auto.

Trường hợp đặc biệt như vậy sẽ dễ bị lỗi và lạm dụng. Bạn cần phải xác định các quy tắc khi bạn đề xuất rằng một cái gì đó như a() sẽ hoạt động. Làm thế nào là operator() tra cứu? Trạng thái chính xác của loại a là gì? Loại sẽ hoàn thành? (có nghĩa là bạn đã biết danh sách chụp của lambda). Một khi bạn đã xây dựng rằng trong một định dạng hợp lý cho một spec, nó sẽ được dễ dàng hơn để làm cho báo cáo về nó.

Cho phép trường hợp sử dụng của bạn sẽ có nghĩa là thêm một trường hợp bạn cần phải quét trước trong mã, vì để xác định loại của a trong a() bạn phải chắc chắn rằng initializer kết thúc mà không có gì có thể "unlambda" kiểu

struct y { void operator()() { } }; 
template<typename T> y operator+(T, y) { return y(); } 
auto x = [] { x(); } + y(); 

Trong trường hợp này, x() sẽ gọi y::operator(), không gọi là lambda.

Vì hiện tại, a chỉ bị cấm được đề cập trong toàn bộ trình khởi tạo của nó. Bởi vì trong C++, autokhông phải là một loại. Nó chỉ đơn thuần là kiểu số chỉ định đứng cho loại được suy ra. Kết quả là, một biểu thức không bao giờ có thể nhập tự động.

+3

" Nếu bạn chụp một tham chiếu, thì bạn sẽ gọi một tham chiếu đang treo khi bạn sao chép lambda và bản gốc đi ra ngoài phạm vi. " - tất nhiên, tất cả các tham chiếu chụp đều dễ bị lỗi/lạm dụng này, vì vậy tôi nghi ngờ rằng nó thúc đẩy ủy ban không cung cấp một trường hợp đặc biệt để cho phép 'tự động a' ở đây bị bắt bằng tham chiếu. –

+0

Cùng với vấn đề chụp 'a', một lý do khác mà không thể gọi đệ quy là đóng cửa là không có cách nào khác để chỉ đối tượng đóng này:' this' chỉ định đối tượng lớp bao quanh, nếu có. –

6

Như tôi thấy sự khác biệt quan trọng giữa trường hợp auto a và trường hợp std::function<void()> a là loại std::function<void()> không biết/quan tâm đến loại chức năng thực mà nó đề cập đến thực sự là gì. Viết:

std::function<void()> a; 

là hoàn toàn tốt đẹp, nơi như:

auto a; 

không khôn ngoan. Vì vậy, khi thời gian đến để tổng hợp việc chụp nếu bạn sử dụng std::function<void()> tất cả những gì cần biết về loại đã được biết, trong khi với auto nó vẫn chưa được biết.

+2

Tôi tin rằng đây là câu trả lời đúng và nếu bạn có thể biết tên của kiểu lambda được tạo bởi trình biên dịch (bạn không thể), thì bạn có thể sử dụng tên đó trong phần tử thay vì 'tự động'. Tuy nhiên, tôi có thể dễ dàng thấy điều này trong danh mục "hành vi không xác định". –

+0

@MichaelPrice - Tôi đã cố gắng tìm hiểu câu nói về "bạn không bao giờ có thể biết tên của một loại lambda" cho câu trả lời này nhưng tôi không thể nhớ phần đó là gì. – Flexo

2

Trong một hàm đệ quy f được xác định bởi f và kiểu trả về của f cũng được xác định bởi f trong trường hợp auto nên nó dẫn đến đệ quy vô hạn.

khi auto cố gắng lấy được loại. decltype (f()) sẽ tiếp tục suy ra một decltype khác (f) `như f có nguồn gốc từ f, ví dụ một cuộc gọi trên bất kỳ đệ quy nào cũng đệ quy. xác định kiểu trả về sẽ chuyển thành đệ quy khi được áp dụng trên hàm đệ quy. trong một hàm đệ quy kết thúc đệ quy có thể được thực hiện trong thời gian chạy. nhưng xác định là tĩnh chỉ

+0

@Neel: Điều này không Có ý nghĩa với tôi. Khi không có câu trả lời trong thân thể, làm thế nào nó có thể trả lại bất cứ điều gì? Bạn ngụ ý rằng 'mức đệ quy sâu' có nghĩa là cơ thể lambda vô cùng lớn đến nỗi nó không thể biết sự tồn tại hoặc không tồn tại của (các) câu lệnh trả về? – Nawaz

+0

@awoodland: Cảm ơn, tôi đã xem xét các nhận xét. @Nawaz: Trong mọi hàm có tồn tại một khả năng toán học để trả về một cú pháp cú pháp quét cho cú pháp 'return' chỉ để hiển thị. –

+3

+1 cho phù hợp với hình ảnh –

Các vấn đề liên quan