2017-06-11 30 views
3

以下C++代碼使編譯器GCC(6.3.0)和Clang(3.8.0)變得瘋狂。Lambda捕獲數組元素失敗

for (auto v : vectors2d) 
    for_each (begin(ret), end(ret), [v[3]] (int &n) { n+= v[3];}); 

雖然下面是細

for (auto v : vectors2d) { 
    auto val = v[3]; 
    for_each (begin(ret), end(ret), [val] (int &n) { n+= val;}); 
} 

我知道在C++ 14我可以這樣做

for (auto v : vectors2d) 
    for_each (begin(ret), end(ret), [val=v[3]] (int &n) { n+= val;}); 

在GCC的錯誤是

expected identifier before '[' token 

鏘說

expected ',' or ']' in lambda capture list 

我的問題是:爲什麼[v[3]]出現在捕獲列表中是非法的?

+3

我很肯定'[val = v [3]]'從C++ 14開始就是合法的。 – Rakete1111

+1

你也可以通過引用來捕獲整個'v'。 –

+0

是的,val = v [3]在C++中是legall 14。 –

回答

7

我的問題是爲什麼在捕獲列表中爲[v[3]]是非法的?

如在5.1.2/1 [expr.prim.lambda]在N4141,在捕獲列表中的項應是一個簡單捕獲INIT-捕獲說明。

前者是任何

  • 標識符
  • &標識符
  • this的,

後者要麼標識符初始化&標識符初始化程序

v[3]不符合上述條件,因此被編譯器正確拒絕。

+1

@gsamaras它是一個標識符,後跟一個初始化程序。像'val = v [3]'一樣,'val'是標識符,'= v [3]'是初始化程序。 –

3

v[3]不是一個變量 - 這是一個複雜的表達式,展開爲*(v + 3)(如果operator[]未超載)。因此,捕獲v[3]與捕獲x * x + y * y的精神非常相似 - 而且這種方法的意義不大。例如。編譯器將不得不接受lambda內的x * x + y * y,但有時會拒絕y * y + x * x,因爲重載運算符不必交換。基本上,你問編譯器:「如果我使用一個與我捕獲的表達式相等的表達式,沒關係,但如果我以其他方式混合變量,則應該給我編譯器錯誤」。

假設v[3]是合法的。然後,以下所有的lambda表達式應該正確編譯:

[v[3]]() { return v[3]; } 
[v[3]]() { return v[2 * 2 - 1]; } 
[v[3]](int x) { assert(x == 3); return v[x]; } 

因此,如果我們想「無效捕獲」是一個編譯器錯誤,編譯器應該能夠以某種方式「證明」我們不打算訪問v中除v[3]以外的任何元素。這比停止問題更困難,所以這是不可能的。

當然,我們可以做一些較不嚴格的限制:例如,只允許v[3],但不允許v[2 * 2 - 1]或創建一些算法來檢測這種「足夠好」的情況,但有時會提供錯誤的否定。我不認爲這是值得的努力 - 你可以隨時「緩存」表達式內的變量,並通過價值捕獲。