Подтвердить что ты не робот

Захват статической переменной по ссылке в С++ 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), 
        [&registry] (Foo* p) {
            registry.erase(p->getName());
            delete p;
        });

    registry.emplace(name,b);
    return b;
}

Насколько я знаю, если проблема GCC, обсуждавшаяся ранее, не является проблемой с точки зрения соответствия С++ 11, этот артефакт тоже не должен быть проблемой. Единственное, что нужно сделать, используя этот хак, - не устанавливать результирующий shared_ptr <T> объект для любого глобального объекта, который может быть разрушен после статической переменной.

Я прав?

4b9b3361

Ответ 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.