2009-06-23 23 views
19

La mayoría de los programadores de C++ como me han hecho el siguiente error en algún momento:¿Hay algún uso para declaraciones de funciones locales?

class C { /*...*/ }; 

int main() { 
    C c();  // declares a function c taking no arguments returning a C, 
      // not, as intended by most, an object c of type C initialized 
      // using the default constructor. 
    c.foo(); // compiler complains here. 

    //... 
} 

Ahora bien, aunque el error es bastante obvio una vez que sepa que me preguntaba si hay algún uso razonable para este tipo de declaración de función local excepto que puede hacerlo, especialmente dado que no hay manera de definir tal función local en el mismo bloque; tienes que definirlo en otro lugar.

Creo que las clases locales de estilo Java son una característica bastante agradable que tiendo a usar a menudo, especialmente el tipo anónimo. Incluso las clases locales de C++ (que pueden tener funciones miembro definidas en línea) tienen algún uso. Pero esta declaración de función local sin definición me parece muy incómoda. ¿Es solo un C-legacy o hay algún caso de uso más profundo del que no tengo conocimiento?

Editar para los no creyentes: C c() es no una declaración de puntero de función.

Este programa

int main() 
{ 
    void g(); 
    cout << "Hello "; 
    g(); 
    return 0; 
} 

void g() 
{ 
    cout << "world." << endl; 
} 

salidas Hello world. Este programa

void fun() 
{ 
    cout << "world." << endl; 
} 

int main() 
{ 
    void g(); 
    g = fun; 
    cout << "Hello "; 
    g(); 
    return 0; 
} 

no compila. gcc se queja:

error: cannot convert 'void()()' to 'void()()' in assignment

Comeau:

error: expression must be a modifiable lvalue
+0

Tenga en cuenta que el mensaje de error GCC es incorrecto (error conocido). Debería emitir "error: no se puede convertir 'void()' en 'void()' en la asignación" pero realmente exige incorrectamente el ID de tipo. (en lugar de "void()()", que sería una función que devuelve una función (tipo ilegal), debería decir "void()") –

+0

pregunta relacionada: http://stackoverflow.com/questions/928992/nested -functions-are-not-allowed-but-why-nested-function-prototypes-are-allowed –

+0

No estoy seguro de que el mensaje de error gcc sea un error; de hecho, puede declarar la función con un par adicional de paréntesis ' void (g)() ', que necesita cuando se trata de punteros a funciones:' void * g() 'es una función que devuelve' void * ', mientras que' void (* g)() 'es un puntero a una función de tipo, bueno, 'void()()', es decir, no toma argumentos y no devuelve nada. – Tobias

Respuesta

2

He deseado declaraciones de funciones locales en C cuando quería pasarlas como argumentos a alguna otra función. Hago esto todo el tiempo en otros idiomas. La razón es encapsular la implementación de estructuras de datos.

E.g. Defino alguna estructura de datos, p. un árbol o un gráfico, y no quiero exponer los detalles de su implementación interna, p. porque es posible que quiera cambiarlo o variarlo. Expongo las funciones de acceso y mutador para sus elementos y una función transversal para iterar sobre los elementos. La función transversal tiene como argumento una función que opera en un elemento; la tarea de la función transversal es ejecutar la función de argumento en cada elemento y posiblemente agregar los resultados de alguna manera. Ahora cuando llamo a la función transversal, la función de argumento suele ser alguna operación especializada que depende del estado local y, por lo tanto, debe definirse como una función local. Tener que presionar la función, con las variables que contienen el estado local, afuera para ser global, o en una clase interna creada específicamente para contenerlas, es muy feo. Pero este es un problema que tengo con C y Java, no con C++.

+0

OK, ¿entonces está diciendo que es un legado de C, que perdió su propósito en C++? – Tobias

+0

No exactamente. En realidad no tengo experiencia suficiente en C++, con o sin plantillas, para poder decir cómo abordaría esta situación. Los iteradores son comunes en C++, pero sobre los métodos de cruce que toman las funciones como argumentos, no estoy tan seguro. – reinierpost

+0

Actualización: vea http://stackoverflow.com/questions/2116128/easier-way-to-do-callbacks-for-vectors-or-maybe-something-else-in-the-stl-c – reinierpost

2

Es un prototipo declaración adelantada. Posiblemente, si tiene muchas funciones locales o funciones locales que se llaman entre sí de manera contraria al orden de definición, puede necesitarlo.

+0

No entiendo su punto aquí: en su caso de uso, simplemente declararía todas las funciones al principio del archivo fuente (o incluso en algún encabezado privado) y luego felizmente implementar lejos. ¿Por qué querría/necesitaría _declarar_ una función dentro de otra función? – Tobias

10

El único uso que se me ocurre es el de reducir el alcance de las declaraciones de funciones:

int main() 
{ 
    void doSomething(); 
    doSomething(); 
    return 0; 
} 

void otherFunc() 
{ 
    doSomething(); // ERROR, doSomething() not in scope 
} 

void doSomething() 
{ 
    ... 
} 

Por supuesto, hay mucho mejores soluciones a este. Si necesita ocultar una función, realmente debe reestructurar su código moviendo las funciones a módulos separados para que las funciones que necesitan llamar a la función que desea ocultar estén todas en el mismo módulo. Luego, puede hacer que la función module-local sea declarada estática (el modo C) o poniéndola dentro de un espacio de nombre anónimo (el modo C++).

2

El único uso razonable que puedo ver es permitir que solo una función en una unidad de compilación conozca una función definida en otra unidad de compilación. Creo que sería un uso razonable, pero es el único en el que puedo pensar, y creo que es excesivo.

+0

Pero, ¿por qué declararía una función dentro de la definición de otra (a diferencia de fuera de la definición de la función o en un encabezado)? – Tobias

+1

Como dije, para que solo esa función pueda verlo. –

-1

Si desea diferenciar entre declarar una función que no toma parámetros y crear una instancia de una clase con el destructor predeterminado, omita los paréntesis.

class C 
{ 
    public: 
    void foo() {} 
}; 


int main() 
{ 
    // declare function d, taking no arguments, returning fresh C 
    C d(); 

    // instantiate class (leave parenthesis out) 
    C c; 
    c.foo(); 

    // call declared function 
    C goo = d(); 

    return 0; 
} 

C d() 
{ 
    C c; 
    // .. 
    return c; 
} 
+1

Mi pregunta no es cómo crear una instancia de un objeto, mi pregunta es, ¿por qué declararías alguna vez la función d de la manera en que lo hiciste (en lugar de declararla fuera de la principal)? – Tobias

0

a veces lo hacen por la misma razón se nos anima a declarar variables justo antes de su primer uso (y no antes), es decir, para mejorar la legibilidad. (Sí, me doy cuenta de que para las variables es más importante porque te alivia de la necesidad de verificar si la variable se usa antes de lo que crees que es su primer uso).Tener el prototipo (especialmente si está más involucrado que solo c()) cerca de la invocación de la función mejora la legibilidad. El hecho de que el caso especial C c() sea engañoso para los humanos es desafortunado, pero la idea general de declarar funciones a nivel local tiene sus méritos.

Por supuesto, esto es cierto también para los nombres de funciones que ya están dentro del alcance. ¿Por qué no redeclarar cada función antes de cada uso? Mi respuesta a eso: haz todo con moderación. Demasiado desorden impide la legibilidad (así como la capacidad de mantenimiento --- si la firma de la función cambia alguna vez). Por lo tanto, aunque no lo descartaría, a veces es útil declarar funciones localmente.

+0

¿Alguna vez lo has hecho? ¿Conoces a alguien que alguna vez haya hecho esto? No, y sería genial si pudieras enlazar a algún ejemplo de _real_world. Esto sería realmente interesante. – Tobias

+0

Recuerdo vagamente haberlo hecho una o dos veces, pero no recuerdo una instancia específica. La cuestión es que la mayoría de las funciones que llamo métodos de clase, por lo que es bastante raro. – Ari

1

Como se dijo en el tercer fragmento de this answer, puede ayudar con el sombreado de alcance.

#include <stdio.h> 

void c(); // Prototype of a function named ``c'' 

int main() { 

    c(); // call the function 

    { // additional scoppe needed for C, not for C++ 
     int c = 0; // shadow the c function with a c integer variable 
     c++; 
     { 
      void c(); // hide the c integer variable back with the c function 
      c(); 
     } 
     ++c; 
    } //! 

    return 0; 
} 

void c() { 
    printf("Called\n"); 
} 

no creo que este uso es probable que sea útil a menudo y hay posibilidades de que no va a surgir en cualquier programa bien diseñado.

Todavía creo que esta es la razón más probable para esa función, la definición de funciones últimamente en cuanto a las variables no suena tan bien.

Cuestiones relacionadas