Захват статической переменной по ссылке в С++ 11 лямбда
Основной вопрос
Я пытаюсь скомпилировать следующий код с 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;
}
И кажется, что это не так, потому что вывод следующий:
$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
Итак, мой первый вопрос:
Является ли это отказом GCC, или код не является законным С++ 11? Это исправлено в любой последней версии GCC?
Использование трюка в shared_ptr factory
Я рассматриваю возможность создания артефакта, основанного на этом принципе, но использующего нелитеральную статическую переменную. Этот артефакт предназначен для factory shared_ptr <T> , которые избегают создания новых объектов T, когда вам нужен только дублированный контейнер shared_ptr для одного и того же экземпляра.
Этот артефакт будет выглядеть так:
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;
}
Насколько я знаю, если проблема GCC, обсуждавшаяся ранее, не является проблемой с точки зрения соответствия С++ 11, этот артефакт тоже не должен быть проблемой. Единственное, что нужно сделать, используя этот хак, - не устанавливать результирующий shared_ptr <T> объект для любого глобального объекта, который может быть разрушен после статической переменной.
Я прав?
Ответы
Ответ 1
Почему вы даже пытаетесь захватить bar
? Это статично. Вам вообще не нужно захватывать его. Только автоматические переменные требуют захвата. Clang выдает жесткую ошибку в вашем коде, а не только предупреждение. И если вы просто удалите &bar
из вашего лямбда-захвата, тогда код отлично работает.
#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;
}
печатает
0
1
2
Ответ 2
В стандарте вы можете записывать переменные с автоматическим временем хранения (или this
, который упоминается как явно захваченный).
Итак, нет, вы не можете сделать это в соответствии со стандартом (или, чтобы ответить на ваш первый вопрос, это недопустимо С++ 11 и не является ошибкой компилятора)
5.1.1/2 Имя в лямбда-захвате должно быть в области видимости в контексте лямбда-выражения и должно быть это или ссылаться на локальный переменной или ссылкой с автоматической продолжительностью хранения.
EDIT: И, как сказал Кевин, вам даже не нужно фиксировать локальный static
.