В лямбда-синтаксисе С++ 11 замыкания в куче?

Лямбда-выражения C++11 великолепны!

Но одна вещь отсутствует, а именно, как безопасно работать с изменяемыми данными.

Следующее будет давать неправильные подсчеты после первого подсчета:

#include <cstdio>
#include <functional>
#include <memory>

std::function<int(void)> f1()
{
    int k = 121;
    return std::function<int(void)>([&]{return k++;});
}

int main()
{
    int j = 50;
    auto g = f1();
    printf("%d\n", g());
    printf("%d\n", g());
    printf("%d\n", g());
    printf("%d\n", g());
}

дает,

$ g++-4.5 -std=c++0x -o test test.cpp && ./test
121
8365280
8365280
8365280

Причина в том, что после возврата f1()kвыходит за рамки, но все еще в стеке. Таким образом, при первом выполнении g()kвсе в порядке, но после этого стек повреждается и kтеряет свое значение.

Таким образом, единственный способ, которым мне удалось сделать безопасные возвращаемые замыкания в C++11, — это явно выделить закрытые переменные в куче:

std::function<int(void)> f2()
{
    int k = 121;
    std::shared_ptr<int> o = std::shared_ptr<int>(new int(k));
    return std::function<int(void)>([=]{return (*o)++;});
}

int main()
{
    int j = 50;
auto g = f2();
    printf("%d\n", g());
    printf("%d\n", g());
    printf("%d\n", g());
    printf("%d\n", g());
}

Здесь [=]используется для обеспечения общий указатель скопирован, а не указан, поэтому обработка памяти выполняется правильно: выделенная в куче копия kдолжна быть освобождена, когда сгенерированная функция gвыходит за рамки. Результат такой, как хотелось бы:

$ g++-4.5 -std=c++0x -o test test.cpp && ./test
121
122
123
124

Довольно уродливо ссылаться на переменные, разыменовывая их, но вместо этого должна быть возможность использовать ссылки:

std::function<int(void)> f3()
{
    int k = 121;
    std::shared_ptr<int> o = std::shared_ptr<int>(new int(k));
    int &p = *o;
    return std::function<int(void)>([&]{return p++;});
}

На самом деле, это странным образом дает мне,

$ g++-4.5 -std=c++0x -o test test.cpp && ./test
0
1
2
3

Есть идеи, почему? Возможно, теперь, когда я думаю об этом, невежливо брать ссылку на общий указатель, поскольку это не отслеживаемая ссылка.Я обнаружил, что перемещение ссылки внутрь лямбды приводит к сбою,

std::function<int(void)> f4()
{
    int k = 121;
std::shared_ptr<int> o = std::shared_ptr<int>(new int(k));
    return std::function<int(void)>([&]{int &p = *o; return p++;});
}

дающему,

g++-4.5 -std=c++0x -o test test.cpp && ./test
156565552
/bin/bash: line 1: 25219 Segmentation fault      ./test

В любом случае было бы неплохо, если бы был способ автоматически создавать безопасно возвращаемые замыкания с помощью распределения кучи. Например, если бы существовала альтернатива [=]и [&], указывающая, что переменные должны размещаться в куче и на них можно ссылаться через ссылки на общие указатели. Моя первоначальная мысль, когда я узнал о std::function, заключалась в том, что он создает объект, инкапсулирующий замыкание, поэтому он может предоставить хранилище для среды замыкания, но мои эксперименты показывают, что это не помогает.

Я думаю, что безопасно возвращаемые замыкания в C++11 будут иметь первостепенное значение для их использования. Кто-нибудь знает, как это можно сделать более элегантно?

21
задан ildjarn 20 May 2012 в 01:50
поделиться