Лямбда-выражения 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 будут иметь первостепенное значение для их использования. Кто-нибудь знает, как это можно сделать более элегантно?