Pregunta

¿Por qué es imposible tener una referencia nula?La única cosa que he encontrado en el Estándar de C++ es esta línea, en 8.3.2.1

Una sentencia declarativa que especifica el tipo de referencia a cv void" está mal formado.

¿Por qué es de esa manera?¿Por qué no puedo escribir un "genérico", función que acepta un void&?

Para que quede claro, yo no tengo ninguna aplicación útil en la mente donde se utiliza una referencia a vacío podría ser mejor que el uso de plantillas, pero tengo curiosidad acerca de los fundamentos para una prohibición de este constructo.


Para aclarar un poco, entiendo que el uso de una referencia a void "como es", sería sin sentido como desreferenciar un puntero a void.Sin embargo, me podían echar una referencia asometype con el fin de usarlo, no?De hecho, no veo por qué el siguiente fragmento de código se puede trabajar...

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

...mientras este no puede:

void foo(void &data)
{
    int &i = reinterpret_cast<int&>(data);
    // do something with i
}
¿Fue útil?

Solución

Si tuviera una referencia al vacío, ¿qué haría con él? No sería un número, un personaje, un puntero, ni nada de eso. 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).

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

Si no 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 que bien puede ser al menos parte de la lógica aquí.

Otros consejos

Pregúntese primero, ¿cómo desreferenciaría 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 vacío es para que podamos decir " ¡Necesito hacer un trabajo de puntero genérico aquí, y no sé ni me importa lo que estoy señalando quot ;. Por definición, el compilador no sabe a qué apunta un vacío *, por lo tanto, no puede desreferenciarlo. Puede, al enviar, pero el compilador no puede.

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

Para hacer referencia a usted, usted, el programador, necesita convertirlo a otro tipo, entonces puede tener una referencia escrita a él.

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

Ruben, ¿alguna idea?

EDITAR: para responder a su edición.

Tome la primera función, donde pasa datos nulos *. los datos son un elemento perfectamente válido, puede calcular con ellos, o si tiene algún registro implementado, puede registrarlo.

logger << data;

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

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

Ahora, el compilador no le permitirá desreferenciar un void * por ningún motivo (no tiene sentido), lo mismo significa una referencia a void & amp; data, excepto que es una referencia < em> está implícitamente desreferenciado todo el tiempo . El compilador no le permitirá desreferenciar un vacío * en una operación, no le permitirá desreferenciarlo constantemente.

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

No puede hacer NADA " para que los datos EXCEPTO tomen su dirección, y hay un método perfectamente bueno y seguro integrado en el idioma para hacerlo, es decir,

void* data;

¿Tiene esto más sentido?

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

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

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


1 Hubieran sido totalmente inútiles.

De hecho, si miramos hacia atrás en los tiempos de C, los punteros nulos 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 convirtieron en la mejor solución para implementar el carácter genérico. Sin embargo, la administración de memoria personalizada aún tenía que ser posible, y la interoperabilidad entre C ++ y C era una preocupación importante, por lo que se mantuvo nulo *. Una referencia nula hipotética no sería de ayuda con la gestión de la memoria, y la genérica ya está cubierta, por lo que básicamente no tendría ningún uso (excepto por la garantía de no nulidad que se describe a continuación).

2 No podrías hacer nada con él

Cuando se usa un puntero vacío, no se le permite desreferenciarlo; transpuesto al caso de referencias, eso significa que no puede usar la referencia nula (siempre hipotética). Entonces

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 de esta manera:

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 : ¡&*dataptr es equivalente a <=>!

Por lo tanto, incluso tomar la dirección implica una desreferenciación, al menos conceptualmente (entre bastidores, 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 nulas son una aberración.

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

OK, una cosa me está molestando 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 se ignora el tipo. Esto parece permisible ya que todavía podemos trabajar con los datos de la dirección; el tipo es algo superfluo (o menos importante) en este contexto. Anular la referencia es malo, porque intentar y acceder a un miembro no tiene sentido, p. p.mem. No sabemos a qué clase referirse, y por lo tanto la memoria a la que saltar, los punteros vtable a seguir.

Sin embargo, parece tener sentido que p por sí solo estaría bien ya que solo se referiría al objeto, pero ninguno de sus datos. No se necesita información de clase para hacerlo, solo la dirección. Entiendo que no hay absolutamente ninguna utilidad para esto, pero es importante para definir cuándo se rompen las cosas. Permitiendo esta noción, una referencia de C ++ (constantemente desreferenciada pero sin acceder a nada), p. void& ref = static_cast< &void >(obj) también tiene sentido, y por lo tanto permitiría referencias nulas. No estoy diciendo que nadie deba hablar con los responsables, sino de un & "; Tiene sentido &"; punto de vista, parece correcto, ¿no?

Como Luc Touraille señaló anteriormente (al menos, esta es mi interpretación), podría implementarse, pero el problema es semántico. La explicación razonable a la que pude llegar fue que, dado que una variable de objeto es & Quot; tag & Quot; para una secuencia de memoria, el tipo tiene un valor semántico importante. Por lo tanto, el puntero, considerado como una variable con un valor de dirección, trata el tipo como algo superfluo, no clave para definirlo.

¿Alguien estaría de acuerdo con eso?

Puede pensar en una referencia como un puntero desreferenciado. Sintácticamente, trata una referencia como si no fuera un puntero: no necesita el operador * para desreferenciarla, y puede usarla. en lugar de - > para acceder a sus miembros.

Sin embargo, no puede desreferenciar un puntero void. Como lo señaló Binary Worrier, intentar hacerlo le dará un error de compilación. Y si no puede tener un puntero de vacío desreferenciado, eso significa que no puede tener una referencia de vacío.

Si lo fueran, estarí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 referencias nulas o nulas debilitaría esa diferencia de los punteros.

Por supuesto, todavía es posible que una referencia haga referencia a un objeto que ya no existe, pero eso es una excepción.

El siguiente es no una defensa de la noción de vacío de referencias.La presento como una anécdota de la vida silvestre.Pregúntate a ti mismo si no huele raro.

Mi empresa fue uno de los primeros en usar C++ comercialmente, y en un principio se compila utilizando Cfront.Los primeros desarrolladores todavía estaban aprendiendo el idioma, y en general, utilizando todos los trucos en el libro (los operadores en todas partes!).He aquí un truco que pensaban que era cool:

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í lo tienen, no un vacío de referencia, sino más bien un tipo de referencia con un potencial void enlace!Un momento de reflexión probablemente debería sugerir las mejores alternativas a este particular lenguaje...

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top