void addDivisorFilter() { int divisor = computeDivisor(); filters.emplace_back([&](int value) {return value % divisor == 0; }); filters.emplace_back([&divisor](int value) {return value % divisor == 0; }); }
void Widget::addFilter2() const { auto divisorCopy = divisor; filters.emplace_back( [divisorCopy](int value) { return value % divisorCopy == 0; }); }
void Widget::addFilter1() const { auto currentObjectPtr = this; filters.emplace_back( [currentObjectPtr](int value) { return value % (currentObjectPtr->divisor) == 0; }); }
void Widget::addFilter() const { // Error, divisor can't be captured. // filters.emplace_back( [divisor](int value) { return value % divisor == 0; } ); auto currentObjectPtr = this; filters.emplace_back( [currentObjectPtr](int value) { return value % currentObjectPtr->divisor == 0; } ); // Correct answer auto divisorCopy = divisor; // Make divisor its own life time instead of depending on Widget's life time. filters.emplace_back( [=](int value) { return value % divisorCopy == 0; } ); static auto divisorS = 100; filters.emplace_back( [=](int value) // captures nothing! { return value % divisorS == 0; } // refers to above static ); ++divisorS; }
// captures nothing. so no copy capture is done. // divisor is static. // divisor is not self-contained. // This lambda doesn't use any non-static local variables. So nothing is captured. // practically speaking, this lambda captures divisor by reference, a direct contradiction to what the default by-value capture clause seems to imply. // If we stay away from default by-value capture clauses, we eliminate the risk of code being misread in this way. void Widget::addFilter4() const { static auto divisor = 4; filters.emplace_back( [=](int value) { return value % divisor == 0; // divisor is not captured!!! }); ++divisor; // modify divisor. }
void Widget::addFilter3() const { // C++ 14 // copy divisor to closure. // generalized lambda capture. // there's no such thing as a default capture mode for a generalized lambda capture. filters.emplace_back( [divisor = divisor](int value) { return value % divisor == 0; }); }
void Widget::addFilter() const { // no divisor is available -- filters.emplace_back([divisor](int value) // implicit use of a raw pointer: this // Inside any Widget member function, for example, compilers internally replace uses divisor with this->divisor. // In the version of Widget::addFilter with a defalut by-value capture. // what's being captured is the Widget's this pointer, not divisor! // so if this pointer is destroyed, a dangling pointer will occur. // compilers treat the code as if it had been written as addFilter1() // we can know that the viability of the closures arising from this lambda is tied to the lifetime of the Widget whose this pointer they contain a copy of. filters.emplace_back([=](int value) {std::cout << "Widget::addFilter(): divisor=" << divisor << std::endl; return value % divisor == 0; }); }
void addFiler() { auto divisor = 3; // filters.emplace_back( [&](int value) { return value % divisor == 0; } // divisor will be dangle // ); // filters.emplace_back( [&divisor](int value) { return value % divisor == 0; } // divisor will be dangle // ); // filters.emplace_back( [=](int value) { return value % divisor == 0; } // divisor can't dangle // ); filters.emplace_back( [divisor](int value) { return value % divisor == 0; } // Might better ); }