Sử dụng biến thành viên trong danh sách chụp lambda bên trong hàm thành viên


145

Đoạn mã sau biên dịch với gcc 4.5.1 nhưng không phải với 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;
}

Đây là lỗi:

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

Vì thế,

1> trình biên dịch nào là đúng?

2> Làm cách nào tôi có thể sử dụng các biến thành viên bên trong lambda trong VS2010?


1
Lưu ý: Đó phải là pair<const int, set<int> >loại cặp thực tế của bản đồ. Nó cũng có thể là một tài liệu tham khảo-const.
Xèo

Liên quan; rất hữu ích: thispulum.com/ Kẻ
Gabriel Staples

Câu trả lời:


157

Tôi tin rằng VS2010 sẽ đúng vào lúc này và tôi sẽ kiểm tra xem tôi có tiện dụng tiêu chuẩn không, nhưng hiện tại tôi không có.

Bây giờ, nó giống hệt như thông báo lỗi nói: Bạn không thể chụp nội dung bên ngoài phạm vi kèm theo của lambda. grid không nằm trong phạm vi bao quanh, nhưng thislà (mỗi tiếp cận với gridthực tế xảy ra như this->gridtrong các chức năng thành viên). Đối với usecase của bạn, chụp thiscác tác phẩm, vì bạn sẽ sử dụng nó ngay lập tức và bạn không muốn sao chépgrid

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

Tuy nhiên, nếu bạn muốn lưu trữ lưới và sao chép lưới để truy cập sau, nơi puzzleđối tượng của bạn có thể đã bị hủy, bạn sẽ cần tạo một bản sao trung gian, cục bộ:

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

Tôi đang đơn giản hóa - Google để "tiếp cận phạm vi" hoặc xem §5.1.2 để biết tất cả các chi tiết chính.


1
Có vẻ như khá hạn chế với tôi. Tôi không hiểu tại sao một trình biên dịch sẽ cần phải ngăn chặn điều đó. Nó hoạt động tốt với liên kết, mặc dù cú pháp là khủng khiếp với toán tử shift trái.
Jean-Simon Brochu

3
Có thể tmplà một const &để gridcắt giảm sao chép? Chúng tôi vẫn muốn có ít nhất một bản sao, bản sao vào lambda ( [tmp]), nhưng không cần bản sao thứ hai.
Aaron McDaid

4
Giải pháp có thể tạo ra một bản sao bổ sung không cần thiết gridmặc dù nó có thể được tối ưu hóa. Ngắn hơn và tốt hơn là: auto& tmp = grid;vv
Tom Swirly

4
Nếu bạn có sẵn C ++ 14, bạn có thể làm [grid = grid](){ std::cout << grid[0][0] << "\n"; }để tránh bản sao thêm
sigy

Nó dường như được sửa trong gcc 4.9 (và gcc 5.4 cho vấn đề đó)error: capture of non-variable ‘puzzle::grid’
BGabor

108

Tóm tắt các phương án:

chụp this:

auto lambda = [this](){};

sử dụng một tài liệu tham khảo địa phương cho các thành viên:

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

ví dụ: https://godbolt.org/g/dEKVGD


5
Điều thú vị là chỉ sử dụng rõ ràng việc chụp với cú pháp khởi tạo mới hoạt động cho điều này (tức là trong C ++ 14 chỉ làm [&grid]vẫn không hoạt động). Rất vui mừng khi biết điều này!
ohruunuruus

1
Tóm tắt tốt. Tôi thấy cú pháp C ++ 14 rất thuận tiện
tuket

22

Tôi tin rằng, bạn cần phải nắm bắt this.


1
Điều này là chính xác, nó sẽ nắm bắt con trỏ này và bạn vẫn có thể chỉ cần tham khảo gridtrực tiếp. Vấn đề là, nếu bạn muốn sao chép lưới? Điều này sẽ không cho phép bạn làm điều đó.
Xèo

9
Bạn có thể, nhưng chỉ theo cách vòng vo: Bạn phải tạo một bản sao cục bộ và chụp trong lambda. Đó chỉ là quy tắc với lambdas, bạn không thể nắm bắt cứng bên ngoài phạm vi kèm theo.
Xèo

Chắc chắn bạn có thể sao chép. Tôi có nghĩa là bạn không thể sao chép nó, tất nhiên.
Michael Krelin - hacker

Những gì tôi mô tả thực hiện một bản sao chụp, thông qua bản sao trung gian địa phương - xem câu trả lời của tôi. Ngoài ra, tôi không biết cách nào để sao chép một biến thành viên.
Xèo

Chắc chắn, nó không sao chép, nhưng không phải là thành viên. Nó liên quan đến hai bản sao trừ khi trình biên dịch thông minh hơn bình thường, tôi đoán vậy.
Michael Krelin - hacker

14

Một phương pháp thay thế giới hạn phạm vi của lambda thay vì cho phép nó truy cập toàn bộ thislà chuyển một tham chiếu cục bộ đến biến thành viên, ví dụ

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

Tôi thích ý tưởng của bạn: sử dụng một biến tham chiếu giả và vượt qua nó để nắm bắt danh sách :)
Emadpres
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.