2011-10-22 68 views
18

可能重複:
Recursive lambda functions in c++0x遞歸調用(C++ 11)

爲什麼我不能叫拉姆達遞歸,如果我把它寫成:

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

它給出編譯錯誤(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 

錯誤是什麼意思?

我明白其中的道理,爲什麼我不能這樣寫:

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

我們不能寫,因爲i的類型必須推斷,從它的初始化,這意味着無法被推斷,如果該類型i本身出現在初始化中(ideone)。但是在lambda的情況下它又有什麼關係?如果我沒有錯,lambda的類型由它的參數和返回類型決定;如果它不返回任何內容,則不依賴於主體(在這種情況下,返回類型推導爲void,而不考慮lambda主體中的其他語句)。

無論如何,我得到了一個解決辦法,我可以使用std::function代替爲:

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

它編譯罰款(ideone)。但我仍然很想知道爲什麼auto版本不能編譯的原因。

+0

我認爲這個問題與你最終隱式捕獲'a'有關,因爲'a'本身最終會成爲'a'的成員,因此會無限遞歸。 – Flexo

+0

@awoodland:這是什麼意思?請詳細說明。 – Nawaz

+3

lambda的類型也取決於捕獲,我懷疑這是問題。如果你不使用'a',那麼編譯器不關心,但是當你這樣做的時候它不知道要使用什麼類型的對象。 –

回答

15

原因是沒有特殊的情況爲auto變量的lambda表達式初始值設定項。

這種特殊情況容易出錯和誤用。當您建議像a()這樣的東西應該工作時,您需要定義規則。 operator()是怎麼查找的? a的類型的確切狀態是什麼?該類型是否完整? (這意味着你已經知道lambda的捕獲列表)。一旦你以一種合理的規格形式制定了這個規範,就可以更容易地做出陳述。

允許你的使用情況就意味着,你需要在代碼掃描進取,因爲確定a()a類型,你必須確保初始化什麼也沒有結束另一起案件中,可以「unlambda中」類型

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

在這種情況下,x()將調用y::operator(),而不是lambda。

就像現在一樣,a在其整個初始化程序中被禁止提及。因爲在C++中,auto而不是類型。它僅僅是一個說明符,表示一個將被推斷的類型。因此,表達式不能有自動

+3

「如果你通過引用捕獲了一個引用,那麼當你拷貝你的lambda並且原始a超出了範圍時,你將會調用一個引用引用。 - 當然,所有通過引用進行捕獲都容易出現這種錯誤/誤用,所以我懷疑它是否會激勵委員會不提供特殊情況來允許通過引用捕獲「auto a」。 –

+0

再加上捕獲'a'的問題,無法遞歸調用閉包的另一個原因是否則無法引用閉包對象:'this'指定封閉類對象(如果有的話)。 –

6

正如我所看到的,auto a案例與std::function<void()> a案例之間的重要區別在於std::function<void()>類型不知道/關心它所指的實際函數的類型究竟是什麼。寫作:

std::function<void()> a; 

是完全正常的,在那裏爲:

auto a; 

變得毫無意義。因此,如果您使用std::function<void()>來綜合捕獲時間,那麼所有需要了解的類型已知,而auto目前還不知道。

+2

我相信這是正確的答案,如果你能夠知道編譯器生成的lambda類型的名稱(你不能),那麼你可以在decl中使用該名稱而不是'auto'。但是,我可以很容易地看到這是「未定義的行爲」類別。 –

+0

@MichaelPrice - 我試圖挖掘出「你永遠不會知道lambda類型的名稱」這個答案的引用,但我不記得它在哪個部分。 – Flexo

2

在一個遞歸函數ff定義和返回的f類型也由fauto情況下確定以便它會導致無限遞歸。

auto試圖派生一個類型。 decltype(f())將進一步推導爲另一個decltype(f)`,因爲f從f導出。任何遞歸調用也是遞歸的。當應用於遞歸函數時,返回類型確定變爲遞歸。在遞歸函數中遞歸的結束可以在運行時完成。但決心只是靜止

+0

@Neel:這不對我有意義。當體內沒有返回聲明時,它怎麼能返回任何東西?你是否暗示「深層遞歸層次」意味着lambda-body無限大,無法知道return語句的存在或不存在? – Nawaz

+0

@awoodland:謝謝,I'vent看了看評論。 @Nawaz:在每一個函數中都存在一個數學可能性,只爲可視化返回'return'語法的語法掃描。 –

+3

+1適合圖片 –