¿Por qué es imposible tener una referencia al vacío?


¿Por qué es imposible tener una referencia a void? Lo único que encontré en el estándar C++ es esta línea, en 8.3.2.1

Un declarador que especifica el tipo "referencia a cv void" está mal formado.

¿Por qué es así? ¿Por qué no puedo escribir una función "genérica" que acepte un void&?

Para ser claros, no tengo ninguna aplicación útil en mente donde usar una referencia a vacío podría ser mejor que usar plantillas, pero tengo curiosidad sobre la razón para prohibir esta construcción.


Para aclarar un poco, entiendo que usar una referencia al vacío "tal cual" sería tan insignificante como desreferenciar un puntero al vacío. Sin embargo, podría lanzarlo a una referencia asometype para usarlo, ¿no podría? De hecho, no veo por qué el siguiente fragmento puede funcionar...

void foo(void *data)
{
    int *i = reinterpret_cast<int*>(data);
    // do something with i
}

...mientras que éste no puede:

void foo(void &data)
{
    int &i = reinterpret_cast<int&>(data);
    // do something with i
}
Author: Luc Touraille, 2009-01-19

10 answers

Si tuvieras una referencia a void, ¿qué harías con ella? No sería un número, o un carácter, o un puntero, o algo así. Su función genérica hipotética no podría realizar ninguna operación en ella, excepto tomar su dirección (y no su tamaño).

"void" tiene dos usos: rechazar cualquier conocimiento de tipo (como en void *), y especificar nada en lugar de algo (void function return). En ninguno de los dos casos es posible decir nada sobre un vacío algo excepto que puede tener una dirección.

Si no se puede pensar en una forma en que algo puede ser útil, y yo no puedo, eso es al menos evidencia de que algo es inútil, y eso bien puede ser al menos parte de la lógica aquí.

 36
Author: David Thornley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-19 15:26:16

Pregúntate primero a ti mismo, ¿cómo des-referenciarías un puntero vacío?

void *p = /*something*/ ;
cout << *p << endl;

El código anterior no tiene sentido, una de las razones por las que tenemos void es para que podamos decir "Necesito hacer un trabajo genérico de puntero aquí, y no sé ni me importa lo que estoy señalando". Por definición, el compilador no sabe a qué apunta un void *, por lo tanto no puede desreferenciarlo. Usted puede-por casting - pero el compilador no puede.

Una referencia a un vacío sufre del mismo problema, por definición el los datos apuntados no tienen un tipo, por lo tanto, no se puede hacer referencia a ellos de ninguna manera significativa.

Para referenciarlo usted - el programador - necesita enviarlo a otro tipo, entonces usted puede tener una referencia escrita a él.

No estoy seguro si expliqué esto tan bien como quería.

Ruben, ¿algún pensamiento?

EDITAR: Para responder a su edición.

Tome la primera función, donde pasa datos void*. los datos son un elemento perfectamente válido, se puede calcular con él, o si algunos registros implementados, puede registrarlo.

logger << data;

Y obtendrá los puntos de datos de dirección a. Si intenta desreferenciar datos, el compilador le dará un error (no tiene compilador de C++ a mano en este momento, por lo que no está seguro del error real). por ejemplo,

void* data = /* some assignment */;
logger << *data; // compiler error.

Ahora, el compilador no le permitirá desreferenciar un void * por ninguna razón (no tiene sentido), lo mismo significa una referencia a void & data, excepto que debido a que es una referencia está implícitamente desreferenciado todos los time . El compilador no le permitirá desreferenciar un void* en una operación, no le permitirá desreferenciarlo constantemente.

void& data = /* some assignment *.;
logger << data; // means same as logger << *data above

No puedes hacer NADA a data EXCEPTO tomar su dirección, y hay un método perfectamente bueno - y seguro - incorporado en el lenguaje para hacer eso, es decir,

void* data;

¿Tiene esto más sentido?

 13
Author: Binary Worrier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-19 16:08:42

Una referencia es una referencia a una instancia de algo. Una instancia de algo no puede ser de tipo void. Cualquier instancia de algo debe tener un tipo específico (y posiblemente tipos base).

 6
Author: ChrisW,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-19 15:15:31

Aquí hay un resumen de las diferentes cosas que se han dicho, y que he pensado.

Dos razones principales por las que no se permite la referencia al vacío


1 Habrían sido totalmente inútiles.

De hecho, si miramos hacia atrás en los tiempos de C, los punteros del vacío tenían dos propósitos:

  • Gestión de memoria (por ejemplo, malloc)
  • Genericidad (funciones de escritura que pueden aceptar cualquier tipo de argumentos)

Cuando salió C++, las plantillas se convirtió en la mejor solución para implementar la genericidad. Sin embargo, la gestión de memoria personalizada todavía tenía que ser posible, y la interoperabilidad entre C++ y C era una preocupación importante, por lo que void* se mantuvo. Una referencia de vacío hipotética no sería de ayuda con la gestión de la memoria, y la genericidad ya está cubierta, por lo que básicamente no tendría casi ningún uso (excepto por la garantía de no nulidad que se describe a continuación).

2 Usted no sería capaz de hacer nada con él

Cuando se utiliza un puntero void, no se le permite desreferenciarlo; transpuesto al caso de referencias, eso significa que no puede usar la referencia void (siempre hipotética). So

void *data = // something
// using *data and data-> is forbidden

void &data = // something
// using data is forbidden

Sin embargo, podríamos pensar en un caso de uso donde la referencia no tendría que ser "desreferenciada" (esta frase es terriblemente incorrecta, pero entiendes mi punto), pero donde solo tomaríamos su dirección . Supongamos que tengo la siguiente función:

void foo(void *dataptr)
{
    assert(dataptr != NULL); // or != 0
    // do something with dataptr
}

Para evitar esta afirmación molesta, podría escribir la función esto camino:

void foo(void &dataref)
{
    void *data = &dataref;
    // do something with data
}

Sin Embargo, para que esto funcione, &dataref debe ser equivalente a dataptr, que no es el caso: &dataref es equivalente a &*dataptr!

Por lo tanto, incluso tomar la dirección implica una desreferenciación, al menos conceptualmente (detrás de las escenas, la primera equivalencia es probablemente cierta, pero a nivel semántico no lo es). En consecuencia, no hay absolutamente ningún uso que podamos hacer de los datos, por lo que las referencias vacías son una aberración.

 3
Author: Luc Touraille,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-19 17:38:05

Técnicamente hablando, todo lo que está garantizado es que una referencia a un objeto es un alias para él. Que debajo del argumento de referencia de la capilla que pasa se hace con punteros es un detalle de la implementación. Esto puede ser confuso debido a que las referencias reutilizan el operador & que también es address-of, pero tenga en cuenta que el operador en realidad tiene diferentes significados en diferentes contextos (en una declaración de variable o parámetro denota un tipo de referencia, de lo contrario es address-of, excepto cuando es bitwise-y). Debido a que técnicamente es solo un alias para un objeto, una referencia es 'siempre desreferenciada' como explicó Worrier.

 2
Author: Joseph Garvin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-19 16:16:41

OK, una cosa me molesta sobre esto. La idea de un void*, como se mencionó anteriormente, es que todavía tiene una variable válida que contiene una dirección, pero el tipo está siendo ignorado. Esto parece permitido ya que todavía podemos trabajar con los datos de dirección - el tipo es algo superfluo (o menos importante) en este contexto. Desreferenciar es malo, porque intentar y acceder a un miembro no tiene sentido, por ejemplo, p.mem. No sabemos a qué clase referirnos, y por lo tanto la memoria a la que saltar, la vtable punteros a seguir.

Sin embargo, entonces parecería tener sentido que p por sí solo estaría bien, ya que solo se referiría al objeto, pero no a ninguno de sus datos. No se necesita información de clase para hacerlo, solo la dirección. Entiendo que no hay absolutamente ningún uso para esto, pero es importante para definir cuando las cosas se descomponen. Permitiendo esta noción, una referencia de C++ (constantemente desreferenciada pero sin acceder a nada) por ejemplo void& ref = static_cast< &void >(obj) también tiene sentido, y por lo tanto permitiría referencias vacías. No lo soy. diciendo que cualquiera debería hablar con los responsables, pero desde el punto de vista de "tener sentido", parecería correcto, ¿no?

Como Luc Touraille señaló anteriormente (al menos, esta es mi interpretación), podría implementarse, pero el tema es semántico. La explicación razonable a la que podría llegar era que dado que una variable objeto es una "etiqueta" para una secuencia de memoria, el tipo es de importante valor semántico. Por lo tanto, el puntero, siendo pensado como una variable con un valor de dirección, trata el tipo como algo superfluo - no es clave para definirlo.

¿Alguien estaría de acuerdo con eso?

 1
Author: Luc Touraille,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-22 08:13:11

Puede pensar en una referencia como un puntero des-referenciado. Sintácticamente tratas una referencia como si no fuera un puntero: no necesitas el operador * para desreferenciarlo, y puedes usarlo . en lugar de - > para acceder a sus miembros.

Sin embargo, no puede desreferenciar un puntero void. Como señaló Binary Worrier tratando de hacer eso le dará un error de compilador. Y si no puedes tener un puntero de vacío desreferenciado, eso significa que no puedes tener una referencia de vacío.

 0
Author: Dima,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-19 17:06:42

Si lo fueran, serían semánticamente no diferenciados de los punteros, y equivaldrían a azúcar sintáctico. Una referencia dice: "me refiero a algo que es de este tipo."Permitir una referencia nula o nula debilitaría esa diferencia con respecto a los punteros.

Concedido, todavía es posible que una referencia se refiera a un objeto que ya no existe, pero eso es una excepción.

 0
Author: JohnMcG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-19 18:39:07

Lo siguiente es no una defensa de la noción de referencias vacías. Lo ofrezco como una anécdota de la naturaleza. Pregúntate si no huele raro.

Mi empresa fue una de las primeras en usar C++ comercialmente, e inicialmente compilado usando Cfront. Los primeros desarrolladores todavía estaban aprendiendo el idioma, y en general utilizando todos los trucos en el libro (operadores en todas partes!). Aquí hay un truco que pensaron que era genial:

void Foo::something(int action, ostream &os = *(ostream *)0)
{
   ostream *os_p = &os;
   if (&os == (ostream *)0) {
      os_p = &cerr;
   }
   // continue with method
}

Así que aquí tienes, no una referencia vacía, sino más bien una referencia mecanografiada con un potencial void vinculante! Un momento de pensamiento probablemente debería sugerir mejores alternativas a este lenguaje en particular...

 0
Author: Don Wakefield,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-20 02:51:37

Void es algo que, por definición, no existe, por lo que no es lógico tener su dirección.

 -2
Author: dmajkic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-19 15:16:39