¿Por qué las referencias no se pueden volver a colocar en C++?

13 minutos de lectura

¿Por que las referencias no se pueden volver a colocar
elfogger

Las referencias de C++ tienen dos propiedades:

  • Siempre apuntan al mismo objeto.
  • No pueden ser 0.

Los punteros son lo contrario:

  • Pueden señalar diferentes objetos.
  • Pueden ser 0.

¿Por qué no hay una “referencia o puntero no anulable y reiniciable” en C++? No puedo pensar en una buena razón por la que las referencias no se puedan volver a colocar.

Editar:
La pregunta surge a menudo porque suelo usar referencias cuando quiero asegurarme de que una “asociación” (estoy evitando las palabras “referencia” o “puntero” aquí) nunca sea inválida.

No creo que alguna vez pensé “genial que esta referencia siempre se refiera al mismo objeto”. Si las referencias se pudieran volver a colocar, aún se podría obtener el comportamiento actual de esta manera:

int i = 3;
int& const j = i;

Esto ya es C++ legal, pero no tiene sentido.

Reitero mi pregunta así: “¿Cuál fue la razón detrás de la ‘una referencia es el diseño del objeto? ¿Por qué se consideró útil tener referencias siempre ser el mismo objeto, en lugar de solo cuando se declara como const?”

Saludos, Félix

  • Sé que es un poco tarde, pero la respuesta de Michael Burr parece ser la más completa. Explica las decisiones filosóficas de diseño de Stroustrup. Todos los demás parecen estar rogando por la discusión (de forma indirecta).

    – jww

    16 de diciembre de 2015 a las 10:57


¿Por que las referencias no se pueden volver a colocar
miguel rebabas

La razón por la que C ++ no le permite volver a vincular referencias se da en “Diseño y evolución de C ++” de Stroustrup:

No es posible cambiar a qué se refiere una referencia después de la inicialización. Es decir, una vez que se inicializa una referencia de C++, no se puede hacer que se refiera a un objeto diferente más adelante; no se puede volver a encuadernar. En el pasado me habían mordido las referencias de Algol68 donde r1=r2 puede asignar a través de r1 al objeto al que se hace referencia o asignar un nuevo valor de referencia al r1 (reencuadernación r1) dependiendo del tipo de r2. Quería evitar tales problemas en C++.

1646956037 84 ¿Por que las referencias no se pueden volver a colocar
rlbond

En C++, a menudo se dice que “la referencia es el objeto”. En cierto sentido, es cierto: aunque las referencias se manejan como punteros cuando se compila el código fuente, la referencia pretende significar un objeto que no se copia cuando se llama a una función. Dado que las referencias no son directamente direccionables ( por ejemplo, las referencias no tienen dirección y devuelve la dirección del objeto), semánticamente no tendría sentido reasignarlos. Además, C++ ya tiene punteros, que maneja la semántica de restablecer.

  • Todavía no entiendo realmente la razón por la cual (en mi opinión) la referencia útil no nula que se puede volver a colocar no está en c ++. Los problemas planteados no parecen difíciles de resolver y creo que aumentaría la fiabilidad del programa. Aceptando esta respuesta debido a los votos.

    – El Fogger

    8 de abril de 2009 a las 23:53

  • Corrección: “aunque las referencias son con frecuencia manejados como punteros”

    – Carreras de ligereza en órbita

    4 oct 2011 a las 9:41


  • “La referencia es el objeto” es INCORRECTO. Excepto en el caso especial de extensión de la vida útil de un temporal, la vida útil de una referencia es distinta de la vida útil del objeto al que se refiere. Si la referencia realmente fuera el objeto, entonces el objeto sería destruido cuando la referencia quedó fuera de alcance (o, por el contrario, viviría tanto como cualquier referencia a ella), y este no es el caso.

    – Ben Voigt

    9 de octubre de 2011 a las 3:49

Porque entonces no tendría un tipo que se pueda volver a colocar que no puede ser 0. A menos que haya incluido 3 tipos de referencias/punteros. Lo que simplemente complicaría el idioma por muy poca ganancia (¿Y entonces, por qué no agregar el cuarto tipo también? ¿Referencia no reiniciable que puede ser 0?)

Una mejor pregunta puede ser, ¿por qué querrías que las referencias se puedan volver a colocar? Si lo fueran, eso los haría menos útiles en muchas situaciones. Sería más difícil para el compilador realizar análisis de alias.

Parece que la razón principal por la que las referencias en Java o C# se pueden volver a colocar es porque hacen el trabajo de los punteros. Señalan objetos. No son alias para un objeto.

¿Cuál debería ser el efecto de lo siguiente?

int i = 42;
int& j = i;
j = 43;

En C++ hoy, con referencias que no se pueden volver a colocar, es simple. j es un alias para i, e i termina con el valor 43.

Si las referencias se hubieran vuelto a colocar, la tercera línea vincularía la referencia j a un valor diferente. Ya no sería el alias i, sino el literal entero 43 (que no es válido, por supuesto). O quizás un ejemplo más simple (o al menos sintácticamente válido):

int i = 42;
int k = 43;
int& j = i;
j = k;

Con referencias reubicables. j apuntaría a k después de evaluar este código. Con las referencias que no se pueden volver a colocar de C++, j todavía apunta a i, y se le asigna el valor 43.

Hacer que las referencias se puedan volver a colocar cambia la semántica del lenguaje. La referencia ya no puede ser un alias para otra variable. En su lugar, se convierte en un tipo de valor independiente, con su propio operador de asignación. Y entonces uno de los usos más comunes de las referencias sería imposible. Y nada se ganaría a cambio. La funcionalidad recientemente adquirida para las referencias ya existía en forma de punteros. Así que ahora tendríamos dos formas de hacer lo mismo, y ninguna forma de hacer lo que hacen las referencias en el lenguaje C++ actual.

  • +1. La parte clave allí es “la funcionalidad recientemente adquirida para las referencias que ya existían en forma de punteros”.

    – j_random_hacker

    8 de abril de 2009 a las 2:57

  • Ahí están ya 3 tipos. El tipo posiblemente nulo, no reajustable es int* const. Por consistencia, debería haber 4, ya que posiblemente nulo y reubicable son bastante ortogonales.

    – MSalters

    8 de abril de 2009 a las 12:50

  • Sí, son ortogonales, pero el lenguaje no tiene que proporcionar todas las combinaciones. La pregunta es, ¿superaría la expresividad añadida la complejidad añadida? C ++ es más que lo suficientemente complejo como es, por lo que necesitan una mejor razón para agregar una función que “todavía no existe”.

    – jalf

    8 de abril de 2009 a las 13:09

  • +1 para “Así que ahora tendríamos dos formas de hacer lo mismo, y ninguna forma de hacer lo que hacen las referencias en el lenguaje C++ actual”.

    – Motor de Tierra

    30 de mayo de 2013 a las 0:19

  • No hay ninguna razón por la que j=k tenga que volver a colocar j. Puede seguir con la semántica actual que tienen las referencias ahora y agregar otra operación/sintaxis para volver a colocar, digamos &j = &k.

    – pavon

    2 de febrero de 2016 a las 15:59

¿Por que las referencias no se pueden volver a colocar
Brian R. Bondy

Una referencia no es un puntero, puede implementarse como un puntero en segundo plano, pero su concepto central no es equivalente a un puntero. Una referencia debe ser mirado así *is* el objeto al que se refiere. Por lo tanto, no puede cambiarlo y no puede ser NULL.

Un puntero es simplemente una variable que contiene una dirección de memoria. El puntero en sí tiene una dirección de memoria propia, y dentro de esa dirección de memoria contiene otra dirección de memoria. que se dice que apunta. Una referencia no es lo mismo, no tiene una dirección propia y, por lo tanto, no se puede cambiar para “retener” otra dirección.

Pienso que el parashift C++ Preguntas frecuentes sobre referencias lo dice mejor:

Nota importante: aunque una referencia a menudo se implementa usando una dirección en el lenguaje ensamblador subyacente, no piense en una referencia como un puntero de aspecto extraño a un objeto. Una referencia es el objeto. No es un puntero al objeto, ni una copia del objeto. es el objeto

y de nuevo en Preguntas frecuentes 8.5 :

A diferencia de un puntero, una vez que una referencia se vincula a un objeto, no se puede “volver a colocar” en otro objeto. La referencia en sí no es un objeto (no tiene identidad; tomar la dirección de una referencia te da la dirección del referente; recuerda: la referencia es su referente).

Una referencia que se puede volver a colocar sería funcionalmente idéntica a un puntero.

Con respecto a la nulabilidad: no puede garantizar que dicha “referencia que se puede volver a colocar” no sea NULL en tiempo de compilación, por lo que cualquier prueba de este tipo debería realizarse en tiempo de ejecución. Puede lograr esto usted mismo escribiendo una plantilla de clase de estilo de puntero inteligente que arroja una excepción cuando se inicializa o se le asigna NULL:

struct null_pointer_exception { ... };

template<typename T>
struct non_null_pointer {
    // No default ctor as it could only sensibly produce a NULL pointer
    non_null_pointer(T* p) : _p(p) { die_if_null(); }
    non_null_pointer(non_null_pointer const& nnp) : _p(nnp._p) {}
    non_null_pointer& operator=(T* p) { _p = p; die_if_null(); }
    non_null_pointer& operator=(non_null_pointer const& nnp) { _p = nnp._p; }

    T& operator*() { return *_p; }
    T const& operator*() const { return *_p; }
    T* operator->() { return _p; }

    // Allow implicit conversion to T* for convenience
    operator T*() const { return _p; }

    // You also need to implement operators for +, -, +=, -=, ++, --

private:
    T* _p;
    void die_if_null() const {
        if (!_p) { throw null_pointer_exception(); }
    }
};

Esto puede ser útil en ocasiones: una función que toma un non_null_pointer<int> El parámetro ciertamente comunica más información a la persona que llama que una función que toma int*.

  • INCORRECTO. Puede garantizar de forma trivial que una referencia que se puede volver a colocar no es nula. Al volver a sentarse, también tomaría un valor l, al igual que con la inicialización. Por ejemplo, int foo[0] = {0}; ref int = foo[0]; referencia =&= foo[1] /* Vuelve a colocar ref, no asigna a foo[0] */

    – MSalters

    8 de abril de 2009 a las 12:55

  • @MSalters: No, requerir lvalues ​​no garantiza que la referencia que se puede volver a colocar no sea nula más de lo que lo hace inicializar una referencia regular; consulte 8.3.2.4. Por ejemplo: “int &r = *static_cast(0);”. Hay un mundo de diferencia entre “x está prohibido” y “x produce un comportamiento indefinido”.

    – j_random_hacker

    9 de abril de 2009 a las 5:35

  • @MSalters: como problema secundario, podría haber redactado su comentario de una manera menos hostil sin sacrificar el contenido de la información. Pero elegiste no hacerlo, ¿por qué?

    – j_random_hacker

    9 de abril de 2009 a las 5:48

  • 8.3.2.4 de hecho establece exactamente lo que dije: “Una referencia nula no puede existir en un programa bien definido (C++)”. El estándar C++ cubre básicamente dos temas: qué es un programa C++ bien definido y cómo se comporta. Se inventaron los “demonios nasales” para hacer explícitos los límites de la norma.

    – MSalters

    9 de abril de 2009 a las 7:45

  • Además, podría haber abreviado con seguridad “INCORRECTO”. todo el camino hasta 0 caracteres.

    – j_random_hacker

    9 de abril de 2009 a las 9:18

1646956038 309 ¿Por que las referencias no se pueden volver a colocar
dhaumann

Curiosamente, muchas respuestas aquí son un poco confusas o incluso no vienen al caso (por ejemplo, no es porque las referencias no puedan ser cero o similares, de hecho, puede construir fácilmente un ejemplo donde una referencia es cero).

La verdadera razón por la que no es posible restablecer una referencia es bastante simple.

  • Los punteros le permiten hacer dos cosas: Cambiar el valor detrás del puntero (ya sea a través del -> o la * operador), y para cambiar el propio puntero (asignación directa =). Ejemplo:

    int a;
    int * p = &a;
    1. Cambiar el valor requiere desreferenciar: *p = 42;
    2. Cambiando el puntero: p = 0;
  • Las referencias le permiten cambiar solo el valor. ¿Por qué? Dado que no hay otra sintaxis para expresar el reinicio. Ejemplo:

    int a = 10;
    int b = 20;
    int & r = a;
    r = b; // re-set r to b, or set a to 20?

En otras palabras, sería ambiguo si se le permitiera restablecer una referencia. Tiene aún más sentido cuando se pasa por referencia:

void foo(int & r)
{
    int b = 20;
    r = b; // re-set r to a? or set a to 20?
}
void main()
{
    int a = 10;
    foo(a);
}

Espero que ayude 🙂

  • INCORRECTO. Puede garantizar de forma trivial que una referencia que se puede volver a colocar no es nula. Al volver a sentarse, también tomaría un valor l, al igual que con la inicialización. Por ejemplo, int foo[0] = {0}; ref int = foo[0]; referencia =&= foo[1] /* Vuelve a colocar ref, no asigna a foo[0] */

    – MSalters

    8 de abril de 2009 a las 12:55

  • @MSalters: No, requerir lvalues ​​no garantiza que la referencia que se puede volver a colocar no sea nula más de lo que lo hace inicializar una referencia regular; consulte 8.3.2.4. Por ejemplo: “int &r = *static_cast(0);”. Hay un mundo de diferencia entre “x está prohibido” y “x produce un comportamiento indefinido”.

    – j_random_hacker

    9 de abril de 2009 a las 5:35

  • @MSalters: como problema secundario, podría haber redactado su comentario de una manera menos hostil sin sacrificar el contenido de la información. Pero elegiste no hacerlo, ¿por qué?

    – j_random_hacker

    9 de abril de 2009 a las 5:48

  • 8.3.2.4 de hecho establece exactamente lo que dije: “Una referencia nula no puede existir en un programa bien definido (C++)”. El estándar C++ cubre básicamente dos temas: qué es un programa C++ bien definido y cómo se comporta. Se inventaron los “demonios nasales” para hacer explícitos los límites de la norma.

    – MSalters

    9 de abril de 2009 a las 7:45

  • Además, podría haber abreviado con seguridad “INCORRECTO”. todo el camino hasta 0 caracteres.

    – j_random_hacker

    9 de abril de 2009 a las 9:18

1646956039 729 ¿Por que las referencias no se pueden volver a colocar
snemarch

¿Probablemente hubiera sido menos confuso nombrar las referencias de C++ como “alias”? Como han mencionado otros, las referencias en C++ deben tenerse en cuenta como La variable se refieren a, no como un puntero/referencia a la variable. Como tal, no puedo pensar en una buena razón por la que deberían ser reiniciable.

cuando se trata de punteros, a menudo tiene sentido permitir nulo como valor (y de lo contrario, probablemente desee una referencia en su lugar). Si desea específicamente no permitir mantener nulo, siempre puede codificar su propio tipo de puntero inteligente;)

  • +1 por la idea del puntero inteligente; Yo hubiera dicho lo mismo. No se consideran nuevas características para el lenguaje si ya es posible lograrlo con el estándar existente.

    – Mark Ransom

    8 de abril de 2009 a las 3:13

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad