Me refiero a algo como:
int main()
{
void a()
{
// code
}
a();
return 0;
}
Me refiero a algo como:
int main()
{
void a()
{
// code
}
a();
return 0;
}
Respuestas:
En las versiones actuales de c ++ (C ++ 11, C ++ 14 y C ++ 17), puede tener funciones dentro de funciones en forma de lambda:
int main() {
// This declares a lambda, which can be called just like a function
auto print_message = [](std::string message)
{
std::cout << message << "\n";
};
// Prints "Hello!" 10 times
for(int i = 0; i < 10; i++) {
print_message("Hello!");
}
}
Lambdas también puede modificar variables locales a través de ** captura por referencia *. Con la captura por referencia, el lambda tiene acceso a todas las variables locales declaradas en el alcance del lambda. Puede modificarlos y cambiarlos normalmente.
int main() {
int i = 0;
// Captures i by reference; increments it by one
auto addOne = [&] () {
i++;
};
while(i < 10) {
addOne(); //Add 1 to i
std::cout << i << "\n";
}
}
C ++ no es compatible con eso directamente.
Dicho esto, puede tener clases locales, y pueden tener funciones (no statico static), por lo que puede obtener esto hasta cierto punto, aunque es un poco un error:
int main() // it's int, dammit!
{
struct X { // struct's as good as class
static void a()
{
}
};
X::a();
return 0;
}
Sin embargo, cuestionaría la praxis. Todo el mundo sabe (bueno, ahora que lo hace, de todos modos :)) C ++ no admite funciones locales, por lo que están acostumbrados a no tenerlas. Sin embargo, no están acostumbrados a ese error. Pasaría bastante tiempo en este código para asegurarme de que realmente solo esté allí para permitir funciones locales. No está bien.
int main()yint main(int argc, char* argv[])
int main()y int main(int argc, char* argv[])debe ser compatible y otros pueden ser compatibles, pero todos tienen return int.
Para todos los efectos, C ++ admite esto a través de lambdas : 1
int main() {
auto f = []() { return 42; };
std::cout << "f() = " << f() << std::endl;
}
Aquí, fhay un objeto lambda que actúa como una función local en main. Se pueden especificar capturas para permitir que la función acceda a objetos locales.
Detrás de escena, fhay un objeto de función (es decir, un objeto de un tipo que proporciona un operator()). El compilador crea el tipo de objeto de función en función de la lambda.
1 desde C ++ 11
+1de mi parte.
Ya se han mencionado las clases locales, pero aquí hay una manera de permitir que aparezcan aún más como funciones locales, utilizando una sobrecarga de operador () y una clase anónima:
int main() {
struct {
unsigned int operator() (unsigned int val) const {
return val<=1 ? 1 : val*(*this)(val-1);
}
} fac;
std::cout << fac(5) << '\n';
}
No aconsejo usar esto, es solo un truco divertido (puede hacer, pero no debería).
Con el surgimiento de C ++ 11 hace un tiempo, ahora puede tener funciones locales cuya sintaxis recuerda un poco a JavaScript:
auto fac = [] (unsigned int val) {
return val*42;
};
operator () (unsigned int val), te falta un paréntesis.
std::sort(), o std::for_each().
autopara declarar la variable. Stroustrup da el ejemplo: function<void(char*b, char*e)> rev=[](char*b, char*e) { if( 1<e-b ) { swap( *b, *--e); rev(++b,e); } };para invertir una cadena dada punteros de inicio y fin.
No.
¿Que estás tratando de hacer?
solución alterna:
int main(void)
{
struct foo
{
void operator()() { int a = 1; }
};
foo b;
b(); // call the operator()
}
Comenzando con C ++ 11, puede usar lambdas adecuadas . Vea las otras respuestas para más detalles.
Respuesta anterior: Puedes, más o menos, pero tienes que hacer trampa y usar una clase ficticia:
void moo()
{
class dummy
{
public:
static void a() { printf("I'm in a!\n"); }
};
dummy::a();
dummy::a();
}
Como otros han mencionado, puede usar funciones anidadas usando las extensiones de lenguaje gnu en gcc. Si usted (o su proyecto) se adhiere a la cadena de herramientas gcc, su código será principalmente portátil en las diferentes arquitecturas a las que apunta el compilador gcc.
Sin embargo, si existe un posible requisito de que necesite compilar código con una cadena de herramientas diferente, entonces me mantendría alejado de tales extensiones.
También pisaría con cuidado al usar funciones anidadas. Son una solución hermosa para administrar la estructura de bloques de código complejos, pero cohesivos (cuyas piezas no están destinadas para uso externo / general). También son muy útiles para controlar la contaminación del espacio de nombres (una preocupación muy real con los complejos naturalmente / clases largas en idiomas detallados).
Pero como cualquier cosa, pueden estar abiertos al abuso.
Es triste que C / C ++ no sea compatible con características como un estándar. La mayoría de las variantes pascales y Ada sí (casi todos los lenguajes basados en Algol sí). Lo mismo con JavaScript. Lo mismo con los idiomas modernos como Scala. Lo mismo con lenguajes venerables como Erlang, Lisp o Python.
Y al igual que con C / C ++, desafortunadamente, Java (con el que gano la mayor parte de mi vida) no lo hace.
Menciono Java aquí porque veo varios carteles que sugieren el uso de clases y métodos de clase como alternativas a las funciones anidadas. Y esa también es la solución típica en Java.
Respuesta corta: no.
Hacerlo tiende a introducir una complejidad artificial innecesaria en una jerarquía de clases. En igualdad de condiciones, lo ideal es tener una jerarquía de clases (y sus espacios de nombres y ámbitos que abarquen) que represente un dominio real lo más simple posible.
Las funciones anidadas ayudan a lidiar con la complejidad "privada" dentro de la función. Al carecer de esas facilidades, uno debería tratar de evitar propagar esa complejidad "privada" dentro del modelo de clase.
En software (y en cualquier disciplina de ingeniería), el modelado es una cuestión de compensaciones. Por lo tanto, en la vida real, habrá excepciones justificadas a esas reglas (o más bien pautas). Proceda con cuidado, sin embargo.
No puede tener funciones locales en C ++. Sin embargo, C ++ 11 tiene lambdas . Las lambdas son básicamente variables que funcionan como funciones.
Una lambda tiene el tipo std::function(en realidad eso no es del todo cierto , pero en la mayoría de los casos se puede suponer que sí). Para usar este tipo, debe hacerlo #include <functional>. std::functiones una plantilla, tomando como argumento de plantilla el tipo de retorno y los tipos de argumento, con la sintaxis std::function<ReturnType(ArgumentTypes). Por ejemplo, std::function<int(std::string, float)>es una lambda que devuelve un inty toma dos argumentos, uno std::stringy uno float. El más común es std::function<void()>, que no devuelve nada y no toma argumentos.
Una vez que se declara una lambda, se llama como una función normal, utilizando la sintaxis lambda(arguments).
Para definir una lambda, use la sintaxis [captures](arguments){code}(hay otras formas de hacerlo, pero no las mencionaré aquí). argumentses qué argumentos toma el lambda y codees el código que debe ejecutarse cuando se llama al lambda. Usualmente pones [=]o [&]como capturas. [=]significa que captura todas las variables en el ámbito en el que el valor se define por valor, lo que significa que mantendrán el valor que tenían cuando se declaró la lambda. [&]significa que captura todas las variables en el alcance por referencia, lo que significa que siempre tendrán su valor actual, pero si se borran de la memoria, el programa se bloqueará. Aquí hay unos ejemplos:
#include <functional>
#include <iostream>
int main(){
int x = 1;
std::function<void()> lambda1 = [=](){
std::cout << x << std::endl;
};
std::function<void()> lambda2 = [&](){
std::cout << x << std::endl;
};
x = 2;
lambda1(); //Prints 1 since that was the value of x when it was captured and x was captured by value with [=]
lambda2(); //Prints 2 since that's the current value of x and x was captured by value with [&]
std::function<void()> lambda3 = [](){}, lambda4 = [](){}; //I prefer to initialize these since calling an uninitialized lambda is undefined behavior.
//[](){} is the empty lambda.
{
int y = 3; //y will be deleted from the memory at the end of this scope
lambda3 = [=](){
std::cout << y << endl;
};
lambda4 = [&](){
std::cout << y << endl;
};
}
lambda3(); //Prints 3, since that's the value y had when it was captured
lambda4(); //Causes the program to crash, since y was captured by reference and y doesn't exist anymore.
//This is a bit like if you had a pointer to y which now points nowhere because y has been deleted from the memory.
//This is why you should be careful when capturing by reference.
return 0;
}
También puede capturar variables específicas especificando sus nombres. Simplemente especificando su nombre los capturará por valor, especificando su nombre con un &antes los capturará por referencia. Por ejemplo, [=, &foo]capturará todas las variables por valor, excepto las fooque se capturarán por referencia, y [&, foo]capturará todas las variables por referencia, excepto las fooque se capturarán por valor. También puede capturar solo variables específicas, por ejemplo [&foo], capturará foopor referencia y no capturará otras variables. También puede capturar ninguna variable utilizando []. Si intenta utilizar una variable en una lambda que no capturó, no se compilará. Aquí hay un ejemplo:
#include <functional>
int main(){
int x = 4, y = 5;
std::function<void(int)> myLambda = [y](int z){
int xSquare = x * x; //Compiler error because x wasn't captured
int ySquare = y * y; //OK because y was captured
int zSquare = z * z; //OK because z is an argument of the lambda
};
return 0;
}
No puede cambiar el valor de una variable que fue capturada por valor dentro de una lambda (las variables capturadas por valor tienen un consttipo dentro de la lambda). Para hacerlo, debe capturar la variable por referencia. Aquí hay un ejemplo:
#include <functional>
int main(){
int x = 3, y = 5;
std::function<void()> myLambda = [x, &y](){
x = 2; //Compiler error because x is captured by value and so it's of type const int inside the lambda
y = 2; //OK because y is captured by reference
};
x = 2; //This is of course OK because we're not inside the lambda
return 0;
}
Además, llamar a lambdas no inicializadas es un comportamiento indefinido y generalmente hará que el programa se bloquee. Por ejemplo, nunca hagas esto:
std::function<void()> lambda;
lambda(); //Undefined behavior because lambda is uninitialized
Ejemplos
Aquí está el código de lo que quería hacer en su pregunta usando lambdas:
#include <functional> //Don't forget this, otherwise you won't be able to use the std::function type
int main(){
std::function<void()> a = [](){
// code
}
a();
return 0;
}
Aquí hay un ejemplo más avanzado de una lambda:
#include <functional> //For std::function
#include <iostream> //For std::cout
int main(){
int x = 4;
std::function<float(int)> divideByX = [x](int y){
return (float)y / (float)x; //x is a captured variable, y is an argument
}
std::cout << divideByX(3) << std::endl; //Prints 0.75
return 0;
}
No, no está permitido. Ni C ni C ++ admiten esta característica de manera predeterminada, sin embargo, TonyK señala (en los comentarios) que hay extensiones para el compilador C de GNU que permiten este comportamiento en C.
Todos estos trucos se ven (más o menos) como funciones locales, pero no funcionan así. En una función local puede usar variables locales de sus súper funciones. Es una especie de semi-globals. Ninguno de estos trucos puede hacer eso. El más cercano es el truco lambda de c ++ 0x, pero su cierre está sujeto al tiempo de definición, no al tiempo de uso.
No puede definir una función libre dentro de otra en C ++.
Permítanme publicar aquí una solución para C ++ 03 que considero lo más limpia posible. *
#define DECLARE_LAMBDA(NAME, RETURN_TYPE, FUNCTION) \
struct { RETURN_TYPE operator () FUNCTION } NAME;
...
int main(){
DECLARE_LAMBDA(demoLambda, void, (){ cout<<"I'm a lambda!"<<endl; });
demoLambda();
DECLARE_LAMBDA(plus, int, (int i, int j){
return i+j;
});
cout << "plus(1,2)=" << plus(1,2) << endl;
return 0;
}
(*) en el mundo C ++ el uso de macros nunca se considera limpio.
Pero podemos declarar una función dentro de main ():
int main()
{
void a();
}
Aunque la sintaxis es correcta, a veces puede conducir al "análisis más irritante":
#include <iostream>
struct U
{
U() : val(0) {}
U(int val) : val(val) {}
int val;
};
struct V
{
V(U a, U b)
{
std::cout << "V(" << a.val << ", " << b.val << ");\n";
}
~V()
{
std::cout << "~V();\n";
}
};
int main()
{
int five = 5;
V v(U(five), U());
}
=> sin salida del programa.
(Solo advertencia de Clang después de la compilación).