I've been playing around with some of the new features in C++11, and I tried to write the following program, expecting it not to work. Much to my surprise, it does (on GCC 4.6.1 on Linux x86 with the 'std=c++0x' flag):
#include <functional>
#include <iostream>
#include <memory>
std::function<int()> count_up_in_2s(const int from) {
std::shared_ptr<int> from_ref(new int(from));
return [from_ref]() { return *from_ref += 2; };
}
int main() {
auto iter_1 = count_up_in_2s(5);
auto iter_2 = count_up_in_2s(10);
for (size_t i = 1; i <= 10; i++)
std::cout << iter_1() << '\t' << iter_2() << '\n'
;
}
I was expecting 'from_ref' to be deleted when each execution of the returned lambda runs. Here's my reasoning: once count_up_in_2s is run, from_ref is popped off the stack, yet because the returned lambda isn't neccessarily ran straight away, since it's returned, there isn't another reference in existence for a brief period until the same reference is pushed back on when the lambda is actually run, so shouldn't shared_ptr's reference count hit zero and then delete the data?
Unless C++11's lambda capturing is a great deal more clever than I'm giving it credit for, which if it is, I'll be pleased. If this is the case, can I assume that C++11's variable capturing will allow all the lexical scoping/closure trickery a la Lisp as long as /something/ is taking care of dynamically allocated memory? Can I assume that all captured references will stay alive until the lambda itself is deleted, allowing me to use smart_ptrs in the above fashion?
If this is as I think it is, doesn't this mean that C++11 allows expressive higher-order programming? If so, I think the C++11 committee did an excellent job =)
The lambda captures from_ref
by value, so it makes a copy. Because of this copy, the ref count is not 0 when from_ref
gets destroyed, it is 1 because of the copy that still exists in the lambda.
The following:
std::shared_ptr<int> from_ref(new int(from));
return [from_ref]() { return *from_ref += 2; };
is mostly equivalent to this:
std::shared_ptr<int> from_ref(new int(from));
class __uniqueLambdaType1432 {
std::shared_ptr<int> capture1;
public:
__uniqueLambdaType1432(std::shared_ptr<int> capture1) :
capture1(capture1) {
}
decltype(*capture1 += 2) operator ()() const {
return *capture1 += 2;
}
};
return __uniqueLambdaType1432(from_ref);
where __uniqueLambdaType1432
is a program-globally unique type distinct from any other, even other lambda types generated by a lexically identical lambda expression. Its actual name is not available to the programmer, and besides the object resulting from the original lambda expression, no other instances of it can be created because the constructor is actually hidden with compiler magic.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With