¿Por qué no puedo definir una función dentro de otra función?

80

Esta no es una pregunta de función lambda, sé que puedo asignar una lambda a una variable.

¿Cuál es el punto de permitirnos declarar, pero no definir una función dentro del código?

Por ejemplo:

#include <iostream>

int main()
{
    // This is illegal
    // int one(int bar) { return 13 + bar; }

    // This is legal, but why would I want this?
    int two(int bar);

    // This gets the job done but man it's complicated
    class three{
        int m_iBar;
    public:
        three(int bar):m_iBar(13 + bar){}
        operator int(){return m_iBar;}
    }; 

    std::cout << three(42) << '\n';
    return 0;
}

Entonces, lo que quiero saber es por qué C ++ permitiría lo twoque parece inútil y lo threeque parece mucho más complicado, pero no lo permite one.

EDITAR:

A partir de las respuestas, parece que la declaración en el código puede evitar la contaminación del espacio de nombres, lo que esperaba escuchar es por qué se ha permitido la capacidad de declarar funciones pero no se ha permitido la capacidad de definir funciones.

Jonathan Mee
fuente
3
La primera onees una definición de función , las otras dos son declaraciones .
Algún tipo programador
9
Creo que entendió mal los términos: quiere preguntar "¿Cuál es el punto de permitirnos declarar, pero no definir una función dentro del código?". Y ya que estamos en eso, probablemente te refieres a "dentro de una función ". Todo es "código".
Peter - Reincorpora a Monica
14
Si se pregunta por qué el lenguaje tiene peculiaridades e inconsistencias: porque evolucionó durante varias décadas, a través del trabajo de muchas personas con muchas ideas diferentes, a partir de lenguajes inventados por diferentes razones en diferentes momentos. Si está preguntando por qué tiene esta peculiaridad: porque nadie (hasta ahora) pensó que las definiciones de funciones locales fueran lo suficientemente útiles para estandarizar.
Mike Seymour
4
@MikeSeymour lo tiene correctamente. C no está tan bien estructurado como, digamos, Pascal, y siempre permite solo definiciones de funciones de nivel superior. Entonces, la razón es histórica, además de la falta de necesidad de cambiarla. Que las declaraciones de funciones sean posibles es solo una consecuencia de que las declaraciones de alcance en general sean posibles. Prohibir eso para funciones hubiera significado una regla adicional.
Peter - Reincorpora a Monica
3
@JonathanMee: Probablemente porque, en general, las declaraciones están permitidas en bloques y no hay ninguna razón en particular para prohibir específicamente las declaraciones de funciones; es más sencillo permitir cualquier declaración sin casos especiales. Pero "por qué" no es realmente una pregunta que se pueda responder; el lenguaje es lo que es porque así evolucionó.
Mike Seymour

Respuestas:

41

No es obvio por qué oneno está permitido; Las funciones anidadas se propusieron hace mucho tiempo en N0295 que dice:

Analizamos la introducción de funciones anidadas en C ++. Las funciones anidadas se comprenden bien y su introducción requiere poco esfuerzo por parte de los proveedores de compiladores, los programadores o el comité. Las funciones anidadas ofrecen ventajas significativas, [...]

Obviamente, esta propuesta fue rechazada, pero dado que no tenemos actas de reuniones disponibles en línea para 1993 , no tenemos una fuente posible para la justificación de este rechazo.

De hecho esta propuesta se apunta en expresiones Lambda y cierres para C ++ como una posible alternativa:

Un artículo [Bre88] y la propuesta N0295 al comité de C ++ [SH93] sugieren agregar funciones anidadas a C ++. Las funciones anidadas son similares a las expresiones lambda, pero se definen como declaraciones dentro del cuerpo de una función, y el cierre resultante no se puede usar a menos que esa función esté activa. Estas propuestas tampoco incluyen agregar un nuevo tipo para cada expresión lambda, sino implementarlas más como funciones normales, lo que incluye permitir que un tipo especial de puntero de función se refiera a ellas. Ambas propuestas son anteriores a la adición de plantillas a C ++, por lo que no mencionan el uso de funciones anidadas en combinación con algoritmos genéricos. Además, estas propuestas no tienen forma de copiar variables locales en un cierre, por lo que las funciones anidadas que producen son completamente inutilizables fuera de su función circundante.

Teniendo en cuenta que ahora tenemos lambdas, es poco probable que veamos funciones anidadas ya que, como se describe en el documento, son alternativas para el mismo problema y las funciones anidadas tienen varias limitaciones en relación con las lambdas.

En cuanto a esta parte de su pregunta:

// This is legal, but why would I want this?
int two(int bar);

Hay casos en los que esta sería una forma útil de llamar a la función que desea. El borrador de la sección estándar de C ++ 3.4.1 [basic.lookup.unqual] nos da un ejemplo interesante:

namespace NS {
    class T { };
    void f(T);
    void g(T, int);
}

NS::T parm;
void g(NS::T, float);

int main() {
    f(parm); // OK: calls NS::f
    extern void g(NS::T, float);
    g(parm, 1); // OK: calls g(NS::T, float)
}
Shafik Yaghmour
fuente
1
Pregunta sobre el ejemplo 3.4.1 que da: ¿No podría la persona que llama en main simplemente escribir ::g(parm, 1)para llamar a la función en el espacio de nombres global? ¿O llamar g(parm, 1.0f);cuál debería resultar en una mejor coincidencia para el deseado g?
Peter - Reincorpora a Monica
@PeterSchneider Hice una declaración demasiado fuerte allí, la ajusté.
Shafik Yaghmour
1
Me gustaría agregar el comentario aquí: esta respuesta no se aceptó porque hizo el mejor trabajo al explicar por qué se permiten las declaraciones de funciones en el código; pero porque hizo el mejor trabajo al describir por qué en el código no se permiten definiciones de funciones, que era la pregunta real. Y específicamente describe específicamente por qué la implementación hipotética de funciones en código diferiría de la implementación de lambdas. +1
Jonathan Mee
1
@JonathanMee: Cómo en el mundo lo hace: "... no tenemos una fuente posible para la justificación de este rechazo". calificar como el mejor trabajo para describir por qué las definiciones de funciones anidadas no están permitidas (¿o incluso intentar describirlas en absoluto?)
Jerry Coffin
@JerryCoffin La respuesta incluyó la justificación oficial de por qué las lambdas ya son un superconjunto de definiciones de funciones de código que hacen que su implementación sea innecesaria: "El cierre resultante no se puede usar a menos que esa función esté activa ... Además, estas propuestas no tienen forma de copiar variables locales en un cierre ". Supongo que está preguntando por qué su análisis de la complejidad adicional impuesta a los compiladores no fue la respuesta que acepté. Si es así: usted habla de la dificultad de algo que las lambdas ya logran, en el código las definiciones claramente podrían implementarse exactamente como las lambdas.
Jonathan Mee
31

Bueno, la respuesta es "razones históricas". En C, podría tener declaraciones de funciones en el alcance del bloque, y los diseñadores de C ++ no vieron el beneficio de eliminar esa opción.

Un ejemplo de uso sería:

#include <iostream>

int main()
{
    int func();
    func();
}

int func()
{
    std::cout << "Hello\n";
}

En mi opinión, esta es una mala idea porque es fácil cometer un error al proporcionar una declaración que no coincide con la definición real de la función, lo que da lugar a un comportamiento indefinido que no será diagnosticado por el compilador.

MM
fuente
10
"Esto es generalmente considerado como una mala idea' - cita requerida.
Richard Hodges
4
@RichardHodges: Bueno, las declaraciones de funciones pertenecen a los archivos de encabezado y la implementación en archivos .co .cpp, por lo que tener estas declaraciones dentro de las definiciones de funciones viola cualquiera de esas dos pautas.
MSalters
2
¿Cómo evita que la declaración sea diferente a la definición?
Richard Hodges
1
@JonathanMee: Estoy diciendo que, si la declaración que estás usando no está disponible donde está definida la función, es posible que el compilador no esté verificando que la declaración coincida con la definición. Por lo tanto, es posible que tenga una declaración local some_type f();y una definición en otra unidad de traducción another_type f() {...}. El compilador no puede decirle que estos no coinciden, y llamar fcon la declaración incorrecta dará un comportamiento indefinido. Por lo tanto, es una buena idea tener solo una declaración, en un encabezado, e incluir ese encabezado donde se define la función, así como donde se usa.
Mike Seymour
6
Creo que lo que está diciendo es que la práctica común de poner declaraciones de funciones en archivos de encabezado es generalmente útil. No creo que nadie esté en desacuerdo con eso. Para lo que no veo ninguna razón es la afirmación de que declarar una función externa en el alcance de la función 'generalmente se considera una mala idea'.
Richard Hodges
23

En el ejemplo que da, void two(int)se declara como una función externa, y esa declaración solo es válida dentro del alcance de la mainfunción .

Eso es razonable si solo desea que el nombre twoesté disponible main()para evitar contaminar el espacio de nombres global dentro de la unidad de compilación actual.

Ejemplo en respuesta a comentarios:

main.cpp:

int main() {
  int foo();
  return foo();
}

foo.cpp:

int foo() {
  return 0;
}

sin necesidad de archivos de encabezado. compilar y vincular con

c++ main.cpp foo.cpp 

se compilará y ejecutará, y el programa devolverá 0 como se esperaba.

Richard Hodges
fuente
¿No twotendría que estar también definido en el archivo causando así la contaminación de todos modos?
Jonathan Mee
1
@JonathanMee no, two()podría definirse en una unidad de compilación completamente diferente.
Richard Hodges
Necesito ayuda para entender cómo funcionaría eso. ¿No tendrías que incluir el encabezado en el que se declaró? En qué punto se declararía, ¿verdad? Simplemente no veo cómo puede definirlo en el código y, de alguna manera, no incluir el archivo que lo declara.
Jonathan Mee
5
@JonathanMee No hay nada especial en los encabezados. Son solo un lugar conveniente para colocar declaraciones. Una declaración dentro de una función es tan válida como una declaración dentro de un encabezado. Entonces, no, no necesitaría incluir el encabezado de lo que está vinculando (puede que ni siquiera haya un encabezado).
Cúbico
1
@JonathanMee En la jerga de C / C ++, la definición y la implementación son lo mismo. Puede declarar una función tantas veces como desee, pero solo puede definirla una vez. No es necesario que la declaración esté en un archivo que termine en .h; puede tener un archivo use.cpp que tenga una barra de funciones que llame a foo (declarando foo en su cuerpo) y un archivo provide.cpp que defina foo, y funcionaría bien siempre que no estropees el paso de vinculación.
Cúbico
19

Puede hacer estas cosas, en gran parte porque en realidad no son tan difíciles de hacer.

Desde el punto de vista del compilador, tener una declaración de función dentro de otra función es bastante trivial de implementar. El compilador necesita un mecanismo que permita declaraciones dentro de funciones para manejar otras declaraciones (por ejemplo, int x;) dentro de una función de todos modos.

Por lo general, tendrá un mecanismo general para analizar una declaración. Para el tipo que escribe el compilador, realmente no importa en absoluto si ese mecanismo se invoca al analizar el código dentro o fuera de otra función; es solo una declaración, por lo que cuando ve lo suficiente para saber que hay una declaración, invoca la parte del compilador que se ocupa de las declaraciones.

De hecho, prohibir estas declaraciones en particular dentro de una función probablemente agregaría complejidad adicional, porque el compilador necesitaría una verificación completamente gratuita para ver si ya está mirando el código dentro de una definición de función y, en base a eso, decidir si permitir o prohibir esta en particular. declaración.

Eso deja la cuestión de en qué se diferencia una función anidada. Una función anidada es diferente debido a cómo afecta la generación de código. En lenguajes que permiten funciones anidadas (por ejemplo, Pascal) normalmente se espera que el código de la función anidada tenga acceso directo a las variables de la función en la que está anidada. Por ejemplo:

int foo() { 
    int x;

    int bar() { 
        x = 1; // Should assign to the `x` defined in `foo`.
    }
}

Sin funciones locales, el código para acceder a las variables locales es bastante simple. En una implementación típica, cuando la ejecución ingresa a la función, se asigna en la pila algún bloque de espacio para las variables locales. Todas las variables locales se asignan en ese único bloque y cada variable se trata simplemente como un desplazamiento desde el principio (o el final) del bloque. Por ejemplo, consideremos una función como esta:

int f() { 
   int x;
   int y;
   x = 1;
   y = x;
   return y;
}

Un compilador (asumiendo que no optimizó el código adicional) podría generar código para esto aproximadamente equivalente a esto:

stack_pointer -= 2 * sizeof(int);      // allocate space for local variables
x_offset = 0;
y_offset = sizeof(int);

stack_pointer[x_offset] = 1;                           // x = 1;
stack_pointer[y_offset] = stack_pointer[x_offset];     // y = x;
return_location = stack_pointer[y_offset];             // return y;
stack_pointer += 2 * sizeof(int);

En particular, tiene una ubicación que apunta al comienzo del bloque de variables locales, y todo acceso a las variables locales es como compensaciones desde esa ubicación.

Con las funciones anidadas, ese ya no es el caso; en cambio, una función tiene acceso no solo a sus propias variables locales, sino a las variables locales de todas las funciones en las que está anidada. En lugar de solo tener un "stack_pointer" desde el cual calcula un desplazamiento, necesita caminar hacia arriba en la pila para encontrar los stack_pointers locales a las funciones en las que está anidado.

Ahora, en un caso trivial, eso tampoco es tan terrible: si barestá anidado dentro de foo, entonces barpuede buscar la pila en el puntero de la pila anterior para acceder a foolas variables. ¿Correcto?

¡Incorrecto! Bueno, hay casos en los que esto puede ser cierto, pero no es necesariamente el caso. En particular,bar podría ser recursivo, en cuyo caso una determinada invocación debarpodría tener que buscar un número casi arbitrario de niveles en la pila para encontrar las variables de la función circundante. En términos generales, debe hacer una de dos cosas: o coloca algunos datos adicionales en la pila, para que pueda buscar una copia de seguridad en la pila en tiempo de ejecución para encontrar el marco de pila de la función que lo rodea, o bien pasa un puntero a el marco de pila de la función circundante como un parámetro oculto para la función anidada. Ah, pero tampoco hay necesariamente una sola función circundante: si puede anidar funciones, probablemente pueda anidarlas (más o menos) arbitrariamente en profundidad, por lo que debe estar listo para pasar una cantidad arbitraria de parámetros ocultos. Eso significa que normalmente termina con algo así como una lista vinculada de marcos de pila a funciones circundantes,

Eso, sin embargo, significa que el acceso a una variable "local" puede no ser un asunto trivial. Encontrar el marco de pila correcto para acceder a la variable puede no ser trivial, por lo que el acceso a las variables de las funciones circundantes también es (al menos normalmente) más lento que el acceso a las variables verdaderamente locales. Y, por supuesto, el compilador tiene que generar código para encontrar los marcos de pila correctos, acceder a las variables a través de cualquiera de un número arbitrario de marcos de pila, etc.

Esta es la complejidad que C evitaba al prohibir las funciones anidadas. Ahora bien, es cierto que un compilador de C ++ actual es un tipo de bestia bastante diferente de un compilador de C antiguo de los años 70. Con cosas como herencia virtual múltiple, un compilador de C ++ tiene que lidiar con cosas en esta misma naturaleza general en cualquier caso (es decir, encontrar la ubicación de una variable de clase base en tales casos también puede ser no trivial). Sobre una base porcentual, admitir funciones anidadas no agregaría mucha complejidad a un compilador de C ++ actual (y algunas, como gcc, ya las admiten).

Al mismo tiempo, tampoco suele aportar mucha utilidad. En particular, si desea definir algo que actúe como una función dentro de una función, puede usar una expresión lambda. Lo que esto crea en realidad es un objeto (es decir, una instancia de alguna clase) que sobrecarga la llamada a la función operator ( operator()) pero aún brinda capacidades similares a funciones. Sin embargo, hace que capturar (o no) datos del contexto circundante sea más explícito, lo que le permite usar los mecanismos existentes en lugar de inventar un mecanismo completamente nuevo y un conjunto de reglas para su uso.

En pocas palabras: aunque inicialmente pueda parecer que las declaraciones anidadas son difíciles y las funciones anidadas son triviales, más o menos lo contrario es cierto: las funciones anidadas son en realidad mucho más complejas de soportar que las declaraciones anidadas.

Jerry Coffin
fuente
5

La primera es una definición de función y no está permitida. Obviamente, wt es el uso de poner una definición de una función dentro de otra función.

Pero los otros dos son solo declaraciones. Imagina que necesitas usar la int two(int bar);función dentro del método principal. Pero se define debajo de la main()función, por lo que la declaración de función dentro de la función te obliga a usar esa función con declaraciones.

Lo mismo se aplica al tercero. Las declaraciones de clase dentro de la función le permiten usar una clase dentro de la función sin proporcionar un encabezado o referencia apropiados.

int main()
{
    // This is legal, but why would I want this?
    int two(int bar);

    //Call two
    int x = two(7);

    class three {
        int m_iBar;
        public:
            three(int bar):m_iBar(13 + bar) {}
            operator int() {return m_iBar;}
    };

    //Use class
    three *threeObj = new three();

    return 0;
}
ANjaNA
fuente
2
¿Qué es "desaceleración"? ¿Te refieres a "declaración"?
Peter Mortensen
4

Esta característica de lenguaje fue heredada de C, donde sirvió para algún propósito en los primeros días de C (¿quizás el alcance de la declaración de funciones?) . No sé si los programadores modernos de C utilizan mucho esta función y lo dudo sinceramente.

Entonces, para resumir la respuesta:

no hay ningún propósito para esta característica en C ++ moderno (que yo sepa, al menos), está aquí debido a la compatibilidad con versiones anteriores de C ++ a C (supongo :)).


Gracias al comentario a continuación:

El prototipo de función tiene como alcance la función en la que se declara, por lo que uno puede tener un espacio de nombres global más ordenado, haciendo referencia a funciones / símbolos externos sin #include.

mr.pd
fuente
el propósito es controlar el alcance del nombre para evitar la contaminación global del espacio de nombres.
Richard Hodges
Ok, supongo que es útil para situaciones en las que desea hacer referencia a funciones / símbolos externos sin contaminar el espacio de nombres global con #include! Gracias por mencionarlo. Haré una edición.
mr.pd
4

En realidad, hay un caso de uso que posiblemente sea útil. Si desea asegurarse de que se llame a una determinada función (y su código se compile), sin importar lo que declare el código circundante, puede abrir su propio bloque y declarar el prototipo de la función en él. (La inspiración es originalmente de Johannes Schaub, https://stackoverflow.com/a/929902/3150802 , a través de TeKa, https://stackoverflow.com/a/8821992/3150802 ).

Esto puede ser especialmente útil si tiene que incluir encabezados que no controla, o si tiene una macro de varias líneas que puede usarse en código desconocido.

La clave es que una declaración local reemplaza a las declaraciones anteriores en el bloque más interno. Si bien eso puede introducir errores sutiles (y, creo, está prohibido en C #), se puede usar conscientemente. Considerar:

// somebody's header
void f();

// your code
{   int i;
    int f(); // your different f()!
    i = f();
    // ...
}

La vinculación puede ser interesante porque es probable que los encabezados pertenezcan a una biblioteca, pero supongo que puede ajustar los argumentos del vinculador para que f() se resuelva en su función cuando se considere esa biblioteca. O le dice que ignore los símbolos duplicados. O no enlaza con la biblioteca.

Peter - Reincorporar a Monica
fuente
Así que ayúdame aquí, ¿dónde quedaría fdefinido en tu ejemplo? ¿No terminaría con un error de redefinición de función ya que estos difieren solo por el tipo de retorno?
Jonathan Mee
@JonathanMee hmmm ... f () podría definirse en una unidad de traducción diferente, pensé. Pero probablemente el enlazador se resistiría si también enlazara con la biblioteca asumida, supongo que tiene razón. Así que no puedes hacer eso ;-), o al menos tienes que ignorar múltiples definiciones.
Peter - Reincorpora a Monica
Mal ejemplo. No hay distinción entre void f()y int f()en C ++ porque el valor de retorno de una función no es parte de la firma de la función en C ++. Cambie la segunda declaración a int f(int)y eliminaré mi voto negativo.
David Hammen
@DavidHammen Intenta compilar i = f();después de declarar void f(). "Sin distinción" es solo la mitad de la verdad ;-). De hecho, utilicé "firmas" de funciones no sobrecargables porque, de lo contrario, toda la circunstancia sería innecesaria en C ++ porque dos funciones con diferentes tipos / números de parámetros podrían coexistir felizmente.
Peter - Reincorpora a Monica
@DavidHammen De hecho, después de leer la respuesta de Shafik, creo que tenemos tres casos: 1. La firma difiere en los parámetros. No hay problema en C ++, la sobrecarga simple y las mejores reglas de coincidencia funcionan. 2. La firma no difiere en absoluto. No hay problema a nivel de idioma; La función se resuelve vinculando con la implementación deseada. 3. La diferencia es solo en el tipo de devolución. No es un problema a nivel de lenguaje, como lo demuestra; la resolución de sobrecarga no funciona; tenemos que declarar una función con una firma diferente y un enlace apropiado.
Peter - Reincorpora a Monica
3

Esta no es una respuesta a la pregunta del OP, sino más bien una respuesta a varios comentarios.

No estoy de acuerdo con estos puntos en los comentarios y respuestas: 1 que las declaraciones anidadas son supuestamente inofensivas, y 2 que las definiciones anidadas son inútiles.

1 El principal contraejemplo de la supuesta inocuidad de las declaraciones de funciones anidadas es el infame Most Vexing Parse . En mi opinión, la extensión de la confusión causada por ella es suficiente para justificar una regla adicional que prohíbe las declaraciones anidadas.

2 El primer contraejemplo de la supuesta inutilidad de las definiciones de funciones anidadas es la necesidad frecuente de realizar la misma operación en varios lugares dentro de exactamente una función. Hay una solución obvia para esto:

private:
inline void bar(int abc)
{
    // Do the repeating operation
}

public: 
void foo()
{
    int a, b, c;
    bar(a);
    bar(b);
    bar(c);
}

Sin embargo, esta solución contamina con bastante frecuencia la definición de clase con numerosas funciones privadas, cada una de las cuales se utiliza exactamente en un llamador. Una declaración de función anidada sería mucho más limpia.

Miguel
fuente
1
Creo que esto es un buen resumen de la motivación de mi pregunta. Si miras la versión original, cité MVP, pero sigo siendo anulado en los comentarios (de mi propia pregunta) y me dicen que MVP es irrelevante :( Simplemente no puedo entender cómo las declaraciones de código potencialmente dañinas todavía están aquí , pero las definiciones de código potencialmente útiles no lo son. Le he dado un +1 para los ejemplos beneficiosos.
Jonathan Mee
2

Respondiendo específicamente a esta pregunta:

A partir de las respuestas, parece que la declaración en el código puede evitar la contaminación del espacio de nombres, lo que esperaba escuchar es por qué se ha permitido la capacidad de declarar funciones pero no se ha permitido la capacidad de definir funciones.

Porque considera este código:

int main()
{
  int foo() {

    // Do something
    return 0;
  }
  return 0;
}

Preguntas para diseñadores de lenguajes:

  1. ¿Debería foo()estar disponible para otras funciones?
  2. Si es así, ¿cuál debería ser su nombre? int main(void)::foo()?
  3. (Tenga en cuenta que 2 no sería posible en C, el creador de C ++)
  4. Si queremos una función local, ya tenemos una forma: convertirla en un miembro estático de una clase definida localmente. Entonces, ¿deberíamos agregar otro método sintáctico para lograr el mismo resultado? ¿Por qué hacer eso? ¿No aumentaría la carga de mantenimiento de los desarrolladores de compiladores de C ++?
  5. Y así...
Richard Hodges
fuente
¿Obviamente este comportamiento está definido para lambdas? ¿Por qué no las funciones definidas en código?
Jonathan Mee
Una lambda es simplemente una forma abreviada de escribir un objeto de función. El caso especial de una lambda que no captura argumentos es equivalente a una definición de función local, al igual que escribir un objeto de función que no tiene miembros de datos.
Richard Hodges
Solo estaba señalando que las lambdas, y en el código, las funciones declaradas ya descartan todos sus puntos. No debería haber aumento de "carga".
Jonathan Mee
@JonathanMee, si le parece bien, envíe un RFC al comité de estándares de c ++.
Richard Hodges
La respuesta de Shafik Yaghmour cubrió que ya se estaba haciendo. Personalmente, me gustaría ver la eliminación de la capacidad de declarar funciones en código si no nos dejan definirlas. Sin embargo, la respuesta de Richard Hodges hace un buen trabajo al explicar por qué todavía necesitamos la capacidad de declarar en la declaración del código.
Jonathan Mee
1

Solo quería señalar que el compilador GCC le permite declarar funciones dentro de funciones. Lea más sobre esto aquí . También con la introducción de lambdas a C ++, esta pregunta es un poco obsoleta ahora.


La capacidad de declarar encabezados de función dentro de otras funciones, la encontré útil en el siguiente caso:

void do_something(int&);

int main() {
    int my_number = 10 * 10 * 10;
    do_something(my_number);

    return 0;
}

void do_something(int& num) {
    void do_something_helper(int&); // declare helper here
    do_something_helper(num);

    // Do something else
}

void do_something_helper(int& num) {
    num += std::abs(num - 1337);
}

¿Qué tenemos aquí? Básicamente, tiene una función que se supone que debe llamarse desde main, por lo que lo que hace es declararla como normal. Pero luego te das cuenta de que esta función también necesita otra función para ayudarlo con lo que está haciendo. Entonces, en lugar de declarar esa función auxiliar por encima de main, la declaras dentro de la función que la necesita y luego se puede llamar desde esa función y solo esa función.

Mi punto es que declarar encabezados de función dentro de funciones puede ser un método indirecto de encapsulación de funciones, que permite que una función oculte algunas partes de lo que está haciendo al delegar en alguna otra función de la que solo él es consciente, casi dando la ilusión de un anidado función .

smac89
fuente
Entendí que podíamos definir una lambda en línea. Entendí que podíamos declarar una función en línea, pero ese es el origen del análisis más molesto , por lo que mi pregunta era si el estándar mantendrá la funcionalidad que solo sirve para inducir ira en los programadores, ¿no deberían los programadores poder definir el también funciona en línea? La respuesta de Richard Hodges me ayudó a comprender el origen de este problema.
Jonathan Mee
0

Las declaraciones de funciones anidadas están permitidas probablemente para 1. Reenviar referencias 2. Para poder declarar un puntero a funciones y pasar otras funciones en un ámbito limitado.

Las definiciones de funciones anidadas no están permitidas probablemente debido a problemas como 1. Optimización 2. Recurrencia (funciones definidas anidadas y adjuntas) 3. Reincorporación 4. Concurrencia y otros problemas de acceso multiproceso.

Desde mi limitado entendimiento :)

Estrella neutrón
fuente