Capturing a static variable by reference in a C++11 lambda
Main question
I'm trying to compile the following code with GCC 4.7.2:
#include <iostream>
int foo() {
static int bar;
return [&bar] () { return bar++; } (); // lambda capturing by reference
}
int main (int argc, char* argv[]) {
std::cout << foo() << std::endl;
return 0;
}
And it seems that is not going well, as the output is this one:
$p2.cpp: In function ‘int foo()’:
$p2.cpp:6:14: warning: capture of variable ‘bar’ with non-automatic storage duration [enabled by default]
$p2.cpp:4:16: note: ‘int bar’ declared here
So, my first question would be:
Is this a failure of GCC, or the code is not legit C++11? Is this fixed in any recent version of GCC?
Using the trick in a shared_ptr factory
I consider to build an artifact based on this principle but using a non-literal static variable. This artifact is meant to be a factory of shared_ptr< T > objects, which avoid the creation of new T objects when you just need a duplicate shared_ptr container for the same instance.
This artifact would look like:
std::shared_ptr<Foo> create(std::string name) {
static std::unordered_map<std::string,std::weak_ptr<Foo>> registry;
if (auto it = registry.find(name) != registry.end())
return registry[name].lock();
auto b = std::shared_ptr<Foo>(
new Foo(name),
[®istry] (Foo* p) {
registry.erase(p->getName());
delete p;
});
registry.emplace(name,b);
return b;
}
As far as I know, if the GCC issue discussed before is not a problem in terms of C++11 conformance, this artifact shouldn't be an issue either. The only thing to take care by using this hack, is to not set the resulting shared_ptr< T > object to any global object which could be destructed after the static variable.
Am I right about this?
Why are you even trying to capture bar
? It's static. You don't need to capture it at all. Only automatic variables need capturing. Clang throws a hard error on your code, not just a warning. And if you simply remove the &bar
from your lambda capture, then the code works perfectly.
#include <iostream>
int foo() {
static int bar;
return [] () { return bar++; } (); // lambda capturing by reference
}
int main (int argc, char* argv[]) {
std::cout << foo() << std::endl;
std::cout << foo() << std::endl;
std::cout << foo() << std::endl;
return 0;
}
prints
0
1
2
Per the standard, you can only capture variables with automatic storage duration (or this
, which is mentioned as explicitly capturable).
So, no, you can't do that as per the standard (or, to answer your first question, that is not valid C++ 11 and is not a compiler bug)
5.1.1/2 A name in the lambda-capture shall be in scope in the context of the lambda expression, and shall be this or refer to a local variable or reference with automatic storage duration.
EDIT: And, as Kevin mentioned, you don't even need to capture a local static
anyways.