2009-05-30 13 views
7

Fui reading la pregunta vinculada que me lleva a hacer esta pregunta.No se permiten las funciones anidadas, pero ¿por qué se permiten los prototipos de función anidada? [C++]

considere el siguiente código

int main() 
{ 
    string SomeString(); 
} 

Todo dice, compilador toma esto como un prototipo de función y no como un objetocadena. Ahora considere el siguiente código.

int main() 
{ 
    string Some() 
    { 
     return ""; 
    } 
} 

El compilador dijo que esto no es válido ya que supongo que la definición de función anidada no está permitida. Si no está permitido, ¿por qué se permiten los prototipos de funciones anidadas? No da ninguna ventaja en lugar de crear confusión (¿o me faltan algunos puntos válidos aquí?).

Me di cuenta de que la siguiente es válida.

int main() 
{ 
    string SomeFun(); 
    SomeFun(); 
    return 0; 
} 

string SomeFun() 
{ 
    std::cout << "WOW this is unexpected" << std::endl; 
} 

Esto también es confuso. Esperaba que la función SomeFun() tenga un alcance solo en principal. Pero estaba equivocado. ¿Por qué el compilador permite compilar código como el anterior? ¿Hay alguna situación en tiempo real donde el código como el anterior tenga sentido?

¿Alguna idea?

+0

+1 Simplemente pulse en la misma consulta, y las respuestas a continuación tienen toda la información y más. – slashmais

Respuesta

8

Su prototipo es simplemente 'Forward Declaration'. Por favor revisa el artículo de Wikipedia.

Básicamente, le dice al compilador "No se alarme si la etiqueta 'SomeFun' se usa de esta manera". Pero su enlazador es el responsable de encontrar el cuerpo de la función correcta.

En realidad, puede declarar un prototipo falso, p. 'char SomeFun()' y úsalo en tu main. Solo obtendrá un error cuando su enlazador intente encontrar el cuerpo de su función falsa. Pero tu compilador estará genial con eso.

Hay muchos beneficios. Debe recordar que el cuerpo de la función no siempre está en el mismo archivo de código fuente. Puede estar en una biblioteca vinculada. Además, esa biblioteca vinculada puede tener una 'firma de enlace' específica. Usar definiciones condicionales puede incluso seleccionar la firma de enlace correcta en tiempo de construcción usando sus prototipos de ámbito. Aunque la mayoría de la gente usaría indicadores de función para eso en cambio.

Espero que esto ayude.

5

Esta es una convención de C, como muchos, que C++ ha adoptado.

La capacidad de declarar una función dentro de otra función en C es una decisión que la mayoría de los programadores probablemente consideren lamentable e innecesaria. Particularmente con el diseño de OOP moderno donde las definiciones de función son comparativamente más pequeñas que en C.

Si desea tener funciones que solo existen en el alcance de otra función, dos opciones son boost::lambda y C++1x lambda.

3

Los prototipos de función son insinuaciones para el compilador.Indican que las funciones se implementan en otro lugar, si no ya se ha descubierto . Nada mas.

3

Cuando declaras un prototipo mientras estás haciendo, básicamente le estás diciendo al compilador que espere a que el enlazador lo resuelva. Dependiendo de dónde escriba el prototipo, se aplican las reglas de alcance. No hay nada técnicamente incorrecto al escribir el prototipo dentro de su función main() (aunque en mi humilde opinión es un poco más desordenado), solo significa que la función solo se conoce localmente dentro de main(). Si hubiera declarado el prototipo en la parte superior de su archivo fuente (o más comúnmente en un archivo de encabezado), el prototipo/función se conocería en toda la fuente.

string foo() 
{ 
    string ret = someString(); // Error 
    return ret; 
} 

int main(int argc,char**argv) 
{ 
    string someString(); 
    string s = somestring(); // OK 
    ... 
} 
7

Como nota al margen, C++ 03 tiene una forma indirecta de definir las funciones locales. Requiere abusar de la característica de clase local:

int main() 
{ 
    struct Local 
    { 
     static string Some() 
     { 
      return ""; 
     } 
    }; 
    std::cout << Local::Some() << std::endl; 
} 
+2

Increíble abuso del idioma. – Joshua

+0

¿Por qué este 'abuso'? (Soy sinceramente curioso por cierto) – Samaursa

5

En cuanto a por qué su declaración de

void f() { 
    void g(); g(); 
} 

es mejor que éste

void g(); 
void f() { 
    g(); 
} 

Por lo general es buena si se mantiene declaraciones como locales como sea posible, para que se produzcan los menos conflictos de nombres posibles. Digo que es discutible si declarar una función localmente (de esta manera) es realmente afortunado, ya que creo que es mejor incluir su encabezado y luego seguir el camino "habitual", lo que también es menos confuso para las personas que no saben de eso . A veces, también es útil para trabajar en torno a una función de sombreado

void f() { 
    int g; 
    // oops, ::g is shadowed. But we can work around that 
    { 
     void g(); g(); 
    } 
} 

Por supuesto, en C++ que podríamos llamar la función g usando its_namespace::g() - pero en los viejos tiempos de C, que no habría sido posible, y esa cosa permitido al programador seguir accediendo a la función. También tenga en cuenta que aunque sintácticamente no es lo mismo, semánticamente lo siguiente también declara una función dentro de un ámbito local, que en realidad se dirige a un alcance diferente.

int main() { 
    using std::exit; 
    exit(); 
} 

Como nota al margen, hay más situaciones como esa, donde el alcance objetivo de la declaración es no el alcance siempre que la declaración aparece en. En general, la entidad se declara convierte en un miembro del alcance en el que aparece la declaración Pero ese no es siempre el caso. Consideremos, por ejemplo, amigo declaraciones, en las que sucede lo

struct X { friend void f() { std::cout << "WoW"; } }; 
int main() { void f(); f(); } // works! 

A pesar de que la declaración de función (y definición!) De f ocurrió dentro del ámbito de X, la entidad (la función en sí) se convirtió en un miembro del espacio de nombres de cerramiento .

+0

Buena explicación. Muchas gracias litb –

+1

+1 Esa última 'cosa de amigo' es rara - simplemente no puedo confiar en mis amigos;) – slashmais

+0

No entiendo el truco de 'amigo '. ¿Hace que '' X :: f() 'pertenezca al espacio de nombres adjunto? – ofavre

Cuestiones relacionadas