2010-01-27 84 views
14

¿Por qué no es posible llamar a una función que no toma argumentos con una llamada de función como argumento que no devuelve ningún valor (que en mi humilde opinión equivale a llamar una función que no toma argumentos sin argumentos).C/C++: Función de llamada sin argumentos con función que no devuelve nada

Por ejemplo:

void foo(void) {...} 
void bar(void) {...} 

foo(bar()) 

No me malinterpreten, yo sé void no es un valor y que no puede ser tratado como tal.

Con mi lógica, tendría sentido y debería ser posible hacerlo. Quiero decir, ¿por qué no? ¿Algún argumento por el que eso no debería ser posible?

+4

¿Por qué querrías hacer esto? –

+0

¿Está confundiendo los conceptos de vacío y nulo? –

+16

Porque 'void foo (void)' significa "una función que no toma argumentos", y no "una función que no toma nada como argumento". – jalf

Respuesta

8

No estoy convencido de que alguna de las razones que he escuchado sean buenas.

Sede, en C++, puede devolver el resultado de una función void:

void foo() { 
    // ... 
} 

void bar() { 
    // ... 
    return foo(); 
} 

Sí, es exactamente lo mismo que:

foo(); 
return; 

pero es mucho más coherente con la programación genérica, de modo que puede hacer que una función de reenvío funcione sin tener que preocuparse de si la función que se reenvía tiene void return.

Por lo tanto, si se aplica un sistema similar para que un retorno void constituya una llamada nulary en un escenario de composición de funciones, eso también podría hacer que la composición de funciones sea más genérica.

+1

¡Buena respuesta! Realmente no lo sabía. Entonces, si eso es posible, ¿por qué no debería ser lo que escribí posible? Es casi lo mismo. – George

+2

de hecho lo haría, pero 'return' es un caso especial por 6.6.3/4:" Una declaración de devolución con una expresión de tipo "cv void" puede usarse solo en funciones con un tipo de retorno de cv void; la expresión es evaluado justo antes de que la función regrese a su llamador ". – Potatoswatter

+0

Exactamente mi punto. :-) –

0

Porque void no le permite especificar un nombre de argumento, que sería necesario para capturar el valor de retorno de la primera función, independientemente de lo que realmente devolvió.

+0

Puede construir un objeto vacío. 'void (3);' es una forma de no hacer nada, y coincidiría con la semántica de los argumentos; se permitió pasar los valores a las funciones 'void'. – Potatoswatter

1

tiene sentido (bar no produce nada, foo no consume nada, por lo tanto, foo(bar()) debería estar permitido). OTOH, sería bueno confundir al lector. si quiere ser l33t, siempre hay operadores ,, && y || para emular el punto y coma.

0

Imaginemos que funciona. Se puede pensar en un solo caso hipotético de que

bar(); 
foo(); 

la que hace exactamente lo mismo, no es mucho más fácil de leer y sensible?

Editado. Sí Sí. :)

+1

Creo que encontrarás esa 'barra(); foo() 'es en realidad el orden correcto de ejecución;) –

+0

En realidad, es' bar(); foo(); 'que hace exactamente lo mismo. Los argumentos se evalúan completamente antes de llamar a la función. – caf

+0

Por el momento tengo algo como esto en mi código: #define EVAL (f) f (++ evalcounter) No me preguntes por qué lo uso, solo tengo que hacerlo. Normalmente, f no toma ningún valor. Acabo de agregar este entero (global) solo para contar algo. ¿No sería mucho mejor si tuviera algo como #define EVAL (f) f (dummy (++ evalcounter))? – George

0

Por el momento tengo algo como esto en mi código: #define EVAL (f) f (++ evalcounter) No me preguntes por qué lo uso, solo tengo que hacerlo. Normalmente f no toma ningún valor. Acabo de agregar este entero (global) solo para contar algo. ¿No sería mucho mejor si tuviera algo como #define EVAL (f) f (dummy (++ evalcounter))?

Ha intentado el operador coma:

#define EVAL(f) (++evalcounter, f()) 
+0

El problema es que antes o después de EVAL puede haber otras variables que estén conectadas a f, por lo que agregar esto destruiría por completo esta conexión. – George

+0

¿Qué quiere decir con 'conectado a f'? Si haces 'a = EVAL (f)', mi sugerencia asignará correctamente el valor de retorno de f a a. –

+0

@George: Creo que deberías abrir una nueva pregunta para eso con ejemplos prácticos de dónde tienes problemas. –

3

Debido a que, de acuerdo con el párrafo 3.9.1/9 de la norma,

Un expresión de tipo void se utilizará sólo como una declaración de la expresión (6.2), como un operando de una expresión coma (5,18), como segunda o tercera operando de?: (5.16), como el operando de typeid, o como la expresión en una declaración de devolución (6.6.3) para una función con el tipo de retorno void.

C/C++ simplemente no está diseñado para ser tan genérico. Usted do obtenga return returns_void(); para la optimización de la cola de llamada, eso es funcional-ish, ¿verdad? : VP

Editar: La regla anterior seguiría permitiendo que llame takes_void(3) con 3 convertidos a void. Esto está prohibido por 8.3.5/2:

Si la declaración cláusula parámetro es vacía, la función no toma ningún argumentos. La lista de parámetros (nulo) es equivalente a la lista de parámetros vacíos . Excepto en este caso especial, void no debe ser un tipo de parámetro (aunque los tipos derivados de vacío, tales como void *, pueden).

+0

Hehe bien al menos algo que podemos hacer :) Por cierto, entiendo que c/C++ no está diseñado para eso. Solo quería escuchar opiniones sobre lo que pensaba. Todavía creo que sería totalmente lógico si esto fuera posible. – George

+0

@George: Creo que la parte más notable es cuántas reglas se requieren para prohibirlo. Tal vez fue posible en compiladores de C antiguos y el comité de C++ consideró que era un estilo pobre. Tener más genéricos hoy da una perspectiva diferente. – Potatoswatter

2

Si la lista (void) parámetro fue tratado de manera uniforme con todas las otras listas de parámetros en C/C++, el significado semántico si tal declaración de parámetros sería "un solo parámetro de tipo void". Si ese fuera el caso, es muy posible que, a los fines de la uniformidad, el lenguaje permita "encadenar" las llamadas a las funciones que se muestran en su ejemplo. (Al menos C++ probablemente lo haría, ya que permite este tipo de uniformidad en las declaraciones return).

Sin embargo, tanto en lenguaje C++ como en C, la lista de parámetros que tiene el formato (void) no se trata uniformemente con otras formas de listas de parámetros. En cambio, tiene un significado especial. Significa que la función tiene sin parámetros en absoluto. (Equivalente a () vacío en C++).

En otras palabras, la función declarada con (void) lista de parámetros toma cero parámetros. Está suministrando uno. Esto es lo que lo hace ilegal. Considerando el significado especial de (void) lista de parámetros en C/C++, permitiendo

foo(bar()); 

no sería muy diferente de lo que permite

foo(bar(), bar()); 
foo(bar(), bar(), bar()); 

también. Cualitativamente, 2 y 3 son tan diferentes de 0, como 1 es.

2

No se deje engañar por la notación. Curiosamente, C usa el prototipo f(void) para indicar "f no espera ningún parámetro" en lugar de "f espera un único parámetro del tipo void".La notación f() se mantuvo como "el número de parámetros que f espera que se desconozca, por lo que puede llamarlo con cualquier número de parámetros que desee, y buena suerte para usted". Antes de ANSI Standard C (también conocido como C89), no existía el prototipo de función, y se necesitaba una herramienta como lint para protegerlo contra incluso los tipos de errores más mundanos con parámetros, como pasar el número incorrecto de parámetros o pasar un parámetro con un tipo salvajemente incompatible.

En cuanto a por qué no puede usar el valor de una función return void como valor, o por qué no puede pasar un parámetro a una función que no espera parámetros, esas restricciones están establecidas para protegerlo de realizar los tipos de errores más mundanos con parámetros.

0

Voy a añadir una declaración adicional: void baz(int);

Ahora, obviamente, si una discusión con el tipo void es igual a sin argumentos, a continuación, dos argumentos, uno de los cuales tiene un tipo de vacío sería igual a un argumento (como 0 = 1 * x < => 1 = 1 + 1 * x)

Por lo tanto, obviamente esto es legal: baz(1, bar());. Pero también (dado que el vacío es "nada") baz(bar(),1);.

Y podemos continuar en este sentido: baz(bar(), 1, bar());. Después de todo, "el vacío no es nada".

O bien lo prohíbe directamente, pone en restricciones arbitrarias, o termina permitiendo construcciones ridículas. Estoy de acuerdo con la elección de la primera resolución

+0

Bueno, sería fácil hacer lo que dije posible y legal y no permitir lo que dijiste. No olvides que las expresiones anteriores están todas separadas por comas, lo que indica que ambas tienen que ser algo. Para explicarme mejor: si "vacío no sería nada", lo anterior equivaldría a baz (1, "nada"), lo cual no es legal porque se espera "algo". – George

Cuestiones relacionadas