Por qué debería void
ser un tipo incompleto?
Hay muchas razones.
Lo más simple es esto: moo FuncName(...)
todavía debe devolver algo. Si es un valor neutral, si es el "valor no es un valor", o lo que sea; todavía debe decir return value;
, donde value
es un valor real. Debe decir return moo();
.
¿Por qué escribir una función que devuelve algo técnicamente, cuando en realidad no está devolviendo algo? El compilador no puede optimizar el retorno porque devuelve un valor de tipo completo. La persona que llama podría usarlo.
C++ no son todas las plantillas, ya sabes. El compilador no necesariamente tiene el conocimiento para tirar todo. A menudo tiene que realizar optimizaciones en función que no tienen conocimiento del uso externo de ese código.
void
en este caso significa "No devuelvo nada". Hay una diferencia fundamental entre eso y "Devuelvo un valor sin sentido". Es legal hacer esto:
moo FuncName(...) { return moo(); }
moo x = FuncName(...);
Esto es en el mejor de los casos engañoso. Una exploración superficial sugiere que se está devolviendo y almacenando un valor. El identificador x
ahora tiene un valor y se puede usar para algo.
Considerando que la presente:
void FuncName(...) {}
void x = FuncName(...);
es un error de compilación. Así es la siguiente:
void FuncName(...) {}
moo x = FuncName(...);
Está claro lo que está pasando: FuncName
no devuelve ningún valor.
Incluso si estuviera diseñando C++ desde cero, sin ninguna retención en off de C, que habría todavía necesita un poco de palabra clave para indicar la diferencia entre una función que devuelve un valor y una que no lo hace.
Además, void*
es especial en parte porque void
no es un tipo completo. Dado que el estándar exige que no sea un tipo completo, el concepto de void*
puede significar "apuntador a la memoria sin tipo". Esto tiene una semántica de casting especializada. Los punteros a la memoria mecanografiada se pueden convertir implícitamente en memoria sin tipo. Los punteros a la memoria sin tipo se pueden convertir explícitamente al puntero tipeado que solía ser.
En su lugar, si usó moo*
, la semántica se vuelve rara. Tiene un puntero a tipeado de memoria. Actualmente, C++ define la conversión entre punteros tipeados no relacionados (a excepción de ciertos casos especiales) como un comportamiento indefinido. Ahora el estándar tiene que agregar otra excepción para el tipo moo
. Se tiene que decir lo que sucede cuando se hace esto:
moo *m = new moo;
*moo;
Con void
, se trata de un error de compilación. ¿Qué pasa con moo
? Todavía es inútil y sin sentido, pero el compilador tiene que permitirlo.
Para ser honesto, yo diría que la mejor pregunta sería "¿Por qué debería void
ser un tipo completo?"
Estoy de acuerdo con casi todo, excepto el último párrafo. ¿Los casos especiales molestos que presenta al tratar con plantillas son intencionales? ¿Para qué? Para molestar a los programadores? –
@ R.MartinhoFernandes: Bueno, no es intencional que nos moleste, pero es intencional en el sentido de que su biblioteca debería ser capaz de tratar con tipos que * no puede instanciar *, 'void' es el ejemplo más destacado. Por ejemplo, si escribiste una plantilla 'memcpy' de tipo seguro que básicamente hace esto:' * to = * from'. Luego, conectar 'void' * debería * hacer que el compilador te llame, no lo acepte silenciosamente, simplemente porque copiar el valor ficticio OPs para' void' ¡no hubiera sido lo que tenías en mente! – bitmask
Oh, entendí mal entonces. –