В Java, когда мне нужна функция обратного вызова, я должен реализовать анонимный класс. Внутри анонимного класса я могу получить доступ к внешним переменным, если они final
,
Теперь я делаю то же самое в C ++. Я понимаю, что C ++ лямбда работает лучше, но иногда мне нужно передать во многих функциях, где с анонимными классами, мне нужно передать только в одном экземпляре.
Я попробовал следующий пример. Работает с GCC 4.3.4.
class IA {
public:
virtual int f(int x) = 0;
};
int main() {
class : public IA {
int f(int x) { return x + 1; }
} a;
doFancyWork(&a);
return 0;
}
Можно ли захватить внешние переменные, как это?
int main() {
int y = 100; // mark y as final if possible
class : public IA {
int f(int x) { return x + y; }
} a;
return 0;
}
ОБНОВИТЬ:
Второй пример не скомпилируется. Ошибки здесь,
prog.cpp: In member function ‘virtual int main()::<anonymous class>::f(int)’:
prog.cpp:9: error: use of ‘auto’ variable from containing function
prog.cpp:7: error: ‘int y’ declared here
prog.cpp: In function ‘int main()’:
prog.cpp:7: warning: unused variable ‘y’
ОБНОВИТЬ:
Я только что понял еще несколько проблем в этом:
Я думаю, что я должен отказаться от анонимных занятий.
Там нет никакого способа автоматически захватить эти переменные, но вы можете использовать альтернативный подход. Это если вы хотите захватить по ссылке:
int main() {
int y = 100; // mark y as final if possible
class IB : public IA {
public:
IB(int& y) : _y(y) {}
int f(int x) { return x + _y; }
private:
int& _y;
} a (y);
return 0;
}
Если вы хотите захватить по значению, просто измените int&
в int
,
В любом случае, вы можете рассмотреть возможность использования кортеж лямбд как объект с несколькими вызовами, если это то, что беспокоит вас об отдельных лямбдах. У вас все еще будет все упаковано в один объект, и захват будет сделан бесплатно.
Просто в качестве примера:
auto callbacks = make_tuple(
[] (int x) { cout << x << endl; },
[&] () { cout << y << endl; }, // y is captured by reference
[=] (int x) { cout << x + y << endl; }, // y is captured by value
// other lambdas here, if you want...
);
Вы можете захватить переменную вручную (что аналогично тому, что делает лямбда-захват за кулисами):
int main() {
int y = 100;
struct {
int& y;
int operator()(int x) { return x + y; }
} anon = { y };
}
Затем вы можете использовать его так:
#include <iostream>
...
std::cout << anon(10) << std::endl;
Печать 110, как и ожидалось. К сожалению, этот метод не может наследовать анонимный тип от другого, так как конструируемые типы из списка инициализаторов не могут наследоваться от другого типа. Если наследование имеет решающее значение, то вы должны использовать метод конструктора, описанный Энди Prowl.
Лямбда C ++ может захватывать «внешние» переменные. [Редактировать: когда я впервые прочитал вопрос, я почему-то упустил, где он упомянул, что знает о лямбдах. Хорошо это или плохо, но в C ++ больше нет ничего похожего на анонимный класс].
Например:
#include <iostream>
int main(){
int y = 100;
auto lambda = [=](int x) { return x + y; };
std::cout << lambda(2);
}
…печать 102
как его вывод.
Обратите внимание, что, хотя это выглядит как функция, лямбда-код C ++ действительно приводит к созданию класса. Полагаю, мне следует добавить: этот класс технически не является анонимным, но у него есть неуказанное имя, которое никогда не отображается напрямую.
Редактировать: Я все еще немного озадачен обоснованием того, что не использовать лямбды, хотя. Намерено ли использовать один класс, который содержит много функций-членов? Если это так, неясно, как вы планируете указать, какую функцию-член вызывать в какое время / для какой цели. Моя немедленная реакция заключается в том, что это звучит подозрительно, как будто вы пытаетесь изменить язык, чтобы поддержать проблемный дизайн.
Это не анонимность класса, который ограничивает доступ к внешним переменным. В вопросе y недоступен, потому что класс был определен локально внутри функции.
Есть несколько ограничений для классов, определенных локально. Во-первых, они могут получить доступ только к локальным переменным, которые являются статическими, но могут получить доступ к любой другой переменной, доступной для области действия функции. Кроме того, локальные классы не могут иметь статических членов данных.
Что касается анонимных классов, у вас не может быть ни конструкторов, ни деструкторов. Все функции-члены должны быть объявлены внутри определения класса. Он не может иметь статические статические члены, это включает в себя постоянные статические интегральные члены, которые обычно могут быть созданы внутри определения класса. Также наследование не допускается.
Анонимные классы являются неясным углом C ++ и имеют небольшую практическую ценность. Лямбда-функции и другие техники намного более гибки. Но кто знает, возможно, в некоторых ситуациях это может помочь с читабельностью кода.
Если твой IA
класс действительно имеет только один виртуальный метод, который вам нужно переопределить (а реальная сложность — другие не виртуальные методы), но вы не хотите захватывать локальные переменные что нужно этому методу, как насчет этого:
int main() {
int y = 100;
auto f = [=](int x){return x+y;};
typedef decltype(f) F;
struct IB : IA {
F _f;
IB(F _f): _f(_f) {}
int f(int x) { return _f(x); }
} a(f);
doFancyWork(&a);
return 0;
}