[C++] EMCP Item 31: Avoid default capture modes
using FilterContainer = :vector<std::function<bool(int)>>;
FilterContainer filters;
filters.emplace_back(
[](int value) { return value % 5 == 0; }
};
5를 인수로 받고자 하면,
void addDivisorFilter() {
...
auto divisor = computeDivisor(calc1, calc2);
filters.emplace_back(
[&](int value) { return value % divisor == 0;}
};
};
divisor가 dangle일 수 있어서 위험!
Lambda는 local variable인 divisor를 참조하는데,
addDivisorFilter리턴 후 local variable이 없어지기에 문제
[&divisor]로 지정해도 마찬가지임
divisor의 수명에 의존한다는 점이 명확히 나타난다는 장점이 있지만 결과는 마찬가지
참조가 대상을 읽는 일도 생가지 않는 방법은?
람다가 의존하는 지역 변수들과 parameter를 명시적으로 나열하는 것이 좋음
기본 capture mode를 사용하면 됨
filters.emplace_back(
[=](int value) { return value % divisor == 0; }
);
단, pointer를 capture하면
그 pointer는 람다에 의해 생성된 closure 안으로 복사되는데,
Lamba 밖의 코드에서 그 pointer를 삭제할 수도 있음 -> dangling 발생
capture는
class Widget {
...
void addFilter() const;
private:
int divisor;
};
void Widget::addFilter() const
{
filters.emplace_back(
[=](int value) { return value % divisor == 0; }
);
static이 아닌 지역 변수(parameter 포함)에만 적용됨
divisor는 지역변수가 아니고 class member이기에 capture 되지 않음
[]로 바꾸면 compile이 되지 않음
[divisor]를 주면, divisor가 local var.가 아니어서 갈무리 못함
this->divisor로,
사실 다음과 같이 처리되기 때문
void Widget::addFilter() const {
auto currentObjectPtr = this;
filters.emplace_back(
[currentObjectPtr](int value)
{ return value % currentObjectPtr->divisor == 0; }
);
};
[=]
자신이 사용하는 모든 객체의 복사본을 만든다고 오해할 수 있음
Things to remember
1) default capture는 참조가 대상을 읽을 수 있음
2) default capture는 pointer(특히 this)가 dangling 될 수 있음
댓글