relaciones - ¿Puedo crear clases anónimas en C++ y capturar las variables externas como en Java?
programacion orientada a objetos (5)
No es el anonimato de la clase lo que restringe el acceso a las variables externas. En la pregunta, y no es accesible porque la clase se definió localmente dentro de una función.
Hay un par de restricciones para las clases definidas localmente. En primer lugar, solo pueden acceder a las variables locales que son estáticas, pero pueden acceder a cualquier otra variable que esté disponible para el alcance de la función. Además, las clases locales no pueden tener miembros de datos estáticos.
En cuanto a las clases anónimas, no puedes tener constructores ni destructores. Todas las funciones miembro deben declararse dentro de la definición de la clase. No puede tener miembros estáticos estáticos, esto incluye miembros integrales estáticos que normalmente se pueden crear instancias dentro de una definición de clase. También la herencia no está permitida.
Las clases anónimas son un rincón oscuro de C ++ y tienen poco valor práctico. Las funciones de Lambda y otras técnicas son mucho más flexibles. Pero quién sabe, quizás en algunas situaciones podría ayudar con la legibilidad del código.
En Java, cuando necesito una función de devolución de llamada, tengo que implementar una clase anónima. Dentro de la clase anónima, puedo acceder a las variables externas si son final
.
Ahora estoy haciendo lo mismo en C ++. Entiendo que C ++ lambda funciona mejor, pero a veces necesito pasar muchas funciones donde con clases anónimas, solo necesito pasar en una instancia.
Probé el siguiente ejemplo. Funciona con 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;
}
¿Es posible capturar las variables externas como esta?
int main() {
int y = 100; // mark y as final if possible
class : public IA {
int f(int x) { return x + y; }
} a;
return 0;
}
ACTUALIZAR:
El segundo ejemplo no se compilará. Los errores están aquí,
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’
ACTUALIZAR:
Me acabo de dar cuenta de algunos problemas más al hacer esto:
- No puedo escribir un constructor porque la clase no tiene un nombre
- la lista de inicializadores no permite la herencia.
- cualquier cambio para hacerlo compilar hace que el código sea ilegible.
Creo que tengo que alejarme de las clases anónimas.
No hay forma de capturar automáticamente esas variables, pero puede usar un enfoque alternativo. Esto es si quieres capturar por referencia:
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;
}
Si desea capturar por valor, simplemente cambie int&
into int
.
De todos modos, puedes considerar usar una tupla de lambdas como un objeto de "devolución de llamada múltiple" si eso es lo que te molesta sobre las lambdas individuales. Todavía tendría todo empacado en un objeto y la captura se haría de forma gratuita.
Solo como un ejemplo:
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...
);
Puede capturar la variable manualmente (que es similar a lo que hace una captura lambda detrás de las escenas):
int main() {
int y = 100;
struct {
int& y;
int operator()(int x) { return x + y; }
} anon = { y };
}
Puede usarlo así:
#include <iostream>
...
std::cout << anon(10) << std::endl;
Imprime 110 como se esperaba. Desafortunadamente no se puede heredar el tipo anónimo de otro con este método ya que los tipos constructables de la lista de inicializadores no pueden heredar de otro tipo. Si la herencia es crucial, entonces debes usar el método constructor descrito por Andy Prowl .
Si su clase IA
realmente tiene solo un método virtual que debe sobrescribir (y la complejidad real es de otros métodos no virtuales) pero no desea capturar las variables locales que este método necesita , ¿qué tal esto ?:
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;
}
Una lambda C ++ puede capturar variables "externas". [Editar: cuando leí la pregunta por primera vez, de alguna manera me perdí donde mencionó que él es consciente de lambdas. Para bien o para mal, C ++ no tiene nada más que realmente se parezca a una clase anónima].
Por ejemplo:
#include <iostream>
int main(){
int y = 100;
auto lambda = [=](int x) { return x + y; };
std::cout << lambda(2);
}
... imprime 102
como salida.
Tenga en cuenta que aunque se parece a una función, una lambda de C ++ realmente da como resultado la creación de una clase. Supongo que debería agregar: esa clase no es técnicamente anónima, pero tiene un nombre no especificado que nunca es directamente visible.
Editar: Todavía estoy un poco desconcertado sobre la justificación para no usar lambdas. ¿La intención es usar una clase que contenga muchas funciones miembro? De ser así, no está claro cómo planea especificar qué función de miembro invocar en qué momento / para qué propósito. Mi reacción inmediata es que esto suena sospechosamente como si estuvieras tratando de torcer el lenguaje para apoyar un diseño problemático.