16 сентября 2010

Local functions in C++

В Pascal есть замечательная вещь - локальные функции. Иногда они ну ооочень удобны. Если кто с Паскалем не знаком, на C++ это бы выглядело примерно так:
int foo(int x)
{
int y = 0;

void bar(int z)
{
y += x*z;
}

bar(1);
bar(2);

return y;
}
То есть тело такой функции пишется внутри другой функции, и она может ссылать на ее переменные.
В C++ такого счастья нет. Может такого не сделали из-за того, что в Сях стек устроен немного по-другому, нежели в Паскале (в любом случае - не оправдание), может Бьярн счел это "не тру". Конечно, можно написать локальный класс, у которого будет нужная функция, но которому придется вручную передавать нужные переменные - но это неудобно, да и выглядит ужасно. Вот как-то так:
int foo(int x)
{
int y = 0;

class foobar
{
public:
bar(int &y, int x) : y(y), x(x)
{
}

void operator ()(int z)
{
y += x*z;
}

private:
int &y, x;
} bar(y, x);

bar(1);
bar(2);

return y;
}
Но с новым стандартом этот способ стал гораздо короче в записи:
int foo(int x)
{
int y = 0;

auto bar = [&](int z)
{
y += x*z;
};

bar(1);
bar(2);

return y;
}
Если сравнить с кодом, приведенным в начале - отличия минимальны! А уж если оно выглядит как утка, плавает как утка и крякает как утка - то чем не утка локальная функция?!

6 комментариев:

wecanstoptrain комментирует...

Это называется closures. Что делать, если ты из корневой функции возвратишь указатель на внутреннюю функцию, и потом где-нибудь попытаешься ее вызвать? Что будет с высвобожденным стеком локальных переменных корневой функции?

Raider комментирует...

Что делать, если ты из корневой функции возвратишь указатель на внутреннюю функцию, и потом где-нибудь попытаешься ее вызвать?

Я же не буду этого делать. К чему этот вопрос?

Eugeniy комментирует...

to wecanstoptrain

Насколько я понимаю, этот код практически эквивалентен коду с ручным созданием класса. Эффекты будут теже, как если вернуть экземпляр foobar. Очевидно, если там есть ссылки, то все будет плохо :)

Кстати, если вернуть указатель на лок функцию в дельфях, то все тоже будет плохо, если она использует локальные переменные корневой функции

master комментирует...

Ассоциация с анонимными методами из C#. Языковое расширение без которого можно прожить :-) Думаю, что такие участки кода многие макросами отрабатывают.

cyann комментирует...

Эта локальная функция называется замыканием (closure).
В новой редакции то, что вы написали (auto bar = ...) называется неименованным фунциональным объектом (lambda).
Вот здесь очень доступно
http://blogs.msdn.com/b/vcblog/archive/2008/10/28/lambdas-auto-and-static-assert-c-0x-features-in-vc10-part-1.aspx
В boost'е есть реализация и closure, и lambda, но пользоваться было не очень удобно и компилировалось не быстро.

Андрей Валяев комментирует...

В си есть nested functions... активно используются например в grub, правда постоянно вызывают ругань компилятора... :)

http://gcc.gnu.org/onlinedocs/gcc/Nested-Functions.html

В C++ имхо функторы всякие лучше будут. :) Доступ к локальным переменным - не нужен, лишнее это.