การใช้ตัวแปรสมาชิกในรายการจับแลมบ์ดาภายในฟังก์ชันสมาชิก


145

โค้ดต่อไปนี้คอมไพล์ด้วย gcc 4.5.1 แต่ไม่ใช่กับ VS2010 SP1:

#include <iostream>
#include <vector>
#include <map>
#include <utility>
#include <set>
#include <algorithm>

using namespace std;
class puzzle
{
        vector<vector<int>> grid;
        map<int,set<int>> groups;
public:
        int member_function();
};

int puzzle::member_function()
{
        int i;
        for_each(groups.cbegin(),groups.cend(),[grid,&i](pair<int,set<int>> group){
                i++;
                cout<<i<<endl;
        });
}
int main()
{
        return 0;
}

นี่เป็นข้อผิดพลาด:

error C3480: 'puzzle::grid': a lambda capture variable must be from an enclosing function scope
warning C4573: the usage of 'puzzle::grid' requires the compiler to capture 'this' but the current default capture mode does not allow it

ดังนั้น,

1> คอมไพเลอร์ตัวไหน

2> ฉันจะใช้ตัวแปรสมาชิกภายในแลมบ์ดาใน VS2010 ได้อย่างไร


1
หมายเหตุ: ควรเป็นpair<const int, set<int> >ของประเภทแผนที่จริง มันควรจะเป็นอ้างอิงถึง const
Xeo

ที่เกี่ยวข้อง มีประโยชน์มาก: thispointer.com/…
Gabriel Staples

คำตอบ:


157

ฉันเชื่อว่า VS2010 ใช้งานได้ดีในเวลานี้และฉันจะตรวจสอบว่าฉันใช้งานมาตรฐานได้หรือไม่ แต่ตอนนี้ฉันไม่ได้ใช้

ตอนนี้มันเหมือนกับข้อความแสดงข้อผิดพลาดที่บอกว่า: คุณไม่สามารถจับภาพสิ่งที่อยู่นอกขอบเขตการปิดล้อมแลมบ์ดาได้ gridไม่ได้อยู่ในขอบเขตการปิดล้อม แต่thisเป็น (ทุกการเข้าถึงgridเกิดขึ้นจริงเหมือนthis->gridในฟังก์ชั่นสมาชิก) สำหรับ usecase ของคุณการจับภาพthisงานเนื่องจากคุณจะใช้งานได้ทันทีและคุณไม่ต้องการคัดลอกgrid

auto lambda = [this](){ std::cout << grid[0][0] << "\n"; }

อย่างไรก็ตามหากคุณต้องการจัดเก็บกริดและคัดลอกเพื่อการเข้าถึงในภายหลังซึ่งpuzzleวัตถุของคุณอาจถูกทำลายไปแล้วคุณจะต้องทำสำเนากลางและโลคอล:

vector<vector<int> > tmp(grid);
auto lambda = [tmp](){}; // capture the local copy per copy

†ฉันลดความซับซ้อนลง - Google สำหรับ "เข้าถึงขอบเขต" หรือดู§5.1.2สำหรับรายละเอียดเต็มไปด้วยเลือดทั้งหมด


1
ดูเหมือนจะค่อนข้าง จำกัด สำหรับฉัน ฉันไม่เข้าใจว่าทำไมคอมไพเลอร์จะต้องป้องกันสิ่งนี้ มันทำงานได้ดีกับการผูกแม้ว่าไวยากรณ์น่ากลัวกับผู้ประกอบการกะซ้าย ostream
Jean-Simon Brochu

3
อาจtmpเป็นconst &ไปgridเพื่อลดการคัดลอก? เรายังต้องการอย่างน้อยหนึ่งสำเนาคัดลอกลงในแลมบ์ดา ( [tmp]) แต่ไม่ต้องการสำเนาที่สอง
Aaron McDaid

4
วิธีแก้ปัญหาอาจทำสำเนาเพิ่มเติมโดยไม่จำเป็นgridแม้ว่าอาจได้รับการปรับปรุงให้ดีที่สุด ที่สั้นกว่าและดีกว่า: auto& tmp = grid;ฯลฯ
Tom Swirly

4
หากคุณมี C ++ 14 พร้อมใช้งานคุณสามารถทำได้[grid = grid](){ std::cout << grid[0][0] << "\n"; }เพื่อหลีกเลี่ยงการคัดลอกพิเศษ
sigy

ดูเหมือนว่าจะได้รับการแก้ไขใน gcc 4.9 (และ gcc 5.4 สำหรับเรื่องนั้น)error: capture of non-variable ‘puzzle::grid’
BGabor

108

บทสรุปของทางเลือก:

การจับกุม this :

auto lambda = [this](){};

ใช้การอ้างอิงในท้องถิ่นกับสมาชิก:

auto& tmp = grid;
auto lambda = [ tmp](){}; // capture grid by (a single) copy
auto lambda = [&tmp](){}; // capture grid by ref

C ++ 14:

auto lambda = [ grid = grid](){}; // capture grid by copy
auto lambda = [&grid = grid](){}; // capture grid by ref

ตัวอย่าง: https://godbolt.org/g/dEKVGD


5
น่าสนใจว่าการใช้การจับภาพด้วยไวยากรณ์เริ่มต้นใช้งานได้อย่างชัดเจนเท่านั้น (เช่นใน C ++ 14 ที่เพิ่งทำ[&grid]ยังใช้งานไม่ได้) ดีใจมากที่รู้เรื่องนี้!
ohruunuruus

1
สรุปที่ดี ฉันพบว่าไวยากรณ์ C ++ 14 สะดวกมาก
tuket

22

thisผมเชื่อว่าคุณจะต้องจับ


1
สิ่งนี้ถูกต้องมันจะจับตัวชี้นี้และคุณยังสามารถอ้างถึงgridได้โดยตรง เกิดปัญหาขึ้นถ้าคุณต้องการคัดลอกกริด สิ่งนี้จะไม่อนุญาตให้คุณทำเช่นนั้น
Xeo

9
คุณทำได้ แต่เป็นไปตามทางอ้อม: คุณต้องทำสำเนาในเครื่องและจับภาพนั้นในแลมบ์ดา นั่นเป็นเพียงกฎกับ lambdas คุณไม่สามารถจับภาพที่แข็งท
Xeo

แน่นอนคุณสามารถคัดลอก ฉันหมายถึงคุณไม่สามารถคัดลอกมันได้แน่นอน
Michael Krelin - แฮ็กเกอร์

สิ่งที่ฉันอธิบายไม่จับการคัดลอกผ่านสำเนากลางท้องถิ่น - ดูคำตอบของฉัน นอกเหนือจากนั้นฉันไม่รู้วิธีคัดลอกการจับตัวแปรสมาชิก
Xeo

แน่นอนว่ามันจะคัดลอกการจับภาพ แต่ไม่ใช่สมาชิก มันเกี่ยวข้องกับสองชุดเว้นแต่คอมไพเลอร์ฉลาดกว่าปกติฉันเดา
Michael Krelin - แฮกเกอร์

14

วิธีทางเลือกที่ จำกัด ขอบเขตของแลมบ์ดาแทนที่จะให้การเข้าถึงทั้งหมดthisเป็นการส่งผ่านการอ้างอิงในท้องถิ่นไปยังตัวแปรสมาชิกเช่น

auto& localGrid = grid;
int i;
for_each(groups.cbegin(),groups.cend(),[localGrid,&i](pair<int,set<int>> group){
            i++;
            cout<<i<<endl;
   });

ฉันรักความคิดของคุณ: ใช้ตัวแปรอ้างอิงปลอมและผ่านมันไปยังรายการจับ :)
Emadpres
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.