멤버 함수 내 람다 캡처 목록에서 멤버 변수 사용


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> >의 실제 쌍 유형 이어야합니다 . 그것은 또한 참조에 대한 참조 일 것입니다.
Xeo

관련; 매우 유용한 정보 : thispointer.com/…
Gabriel Staples

답변:


157

나는 VS2010이 이번에 옳다고 생각하고 표준이 편리한 지 확인하고 있지만 현재는 그렇지 않습니다.

이제는 오류 메시지의 내용과 동일합니다. 람다의 범위를 벗어나는 것은 캡처 할 수 없습니다. grid 는 둘러싸는 범위에 있지 않지만 this( 멤버 함수에서 grid와 같이 실제로 액세스 할 때마다 this->grid) 있습니다. 사용 사례의 경우 캡처 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 left shift 연산자에서 끔찍하지만 bind와 잘 작동합니다.
Jean-Simon Brochu

3
tmpA가 될 const &하기 위해 grid복사 줄이려고? 우리는 여전히 적어도 하나의 사본, 람다 ( [tmp]) 로의 사본을 원 하지만 두 번째 사본은 필요하지 않습니다.
Aaron McDaid

4
솔루션은 grid아마도 최적화되어 있지만 불필요한 추가 사본을 만들 수 있습니다 . 더 짧고 더 낫습니다 : auto& tmp = grid;etc.
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 문법이 매우 편리
하다는 것을 알았습니다

22

나는 당신이 캡처해야한다고 생각합니다 this.


1
이것은 정확합니다.이 포인터를 캡처하고 여전히 grid직접 참조 할 수 있습니다. 문제는 그리드를 복사하려면 어떻게해야합니까? 이것은 당신이 그렇게 할 수 없습니다.
Xeo

9
로터리 방식으로 만 가능합니다. 로컬 복사본을 만들어 람다에서 캡처 해야 합니다. 그것은 람다의 규칙 일뿐입니다. 봉투 범위 밖에서 뻣뻣한 것을 포착 할 수는 없습니다.
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.