С++ 11 lambdas великолепны!
Но не хватает одной вещи, которая заключается в том, как безопасно обрабатывать изменяемые данные.
Ниже перечислены плохие счета после первого счета:
#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
теряет свое значение.
Таким образом, единственный способ, которым я смог сделать безопасное возвратное закрытие в С++ 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
, заключалась в том, что она создает объект, инкапсулирующий замыкание, поэтому он может обеспечить хранение для среды закрытия, но мои эксперименты показывают, что это, похоже, не помогает.
Я думаю, что безопасно возвратные закрытия в С++ 11 будут иметь первостепенное значение для их использования, кто-нибудь знает, как это можно сделать более элегантно?