2010-08-02 14 views
8

Me encontré con mi primer compilador que cambia el lvalue pasado a :: eliminar, pero no pone a cero el lvalue. Ese es el siguiente es cierto:¿En qué parte del estándar C++ dice :: delete puede cambiar lvalues?

Foo * p = new Foo(); 
Foo * q = p; 
assert(p != 0); 
assert(p == q); 
::delete p; 
assert(p != q); 
assert(p != 0); 

Tenga en cuenta que p no es cero después de la operación de eliminación, y ha cambiado desde su valor anterior. Un compañero de trabajo me dijo que esto no es inusual en su experiencia al haber trabajado con algunos compiladores de C++ de mainframes que cambiarían p a 0xFFFFFFFF, así como a otros compiladores que cambiarían p a 0.

Where in the C++ Standard dice que un compilador puede hacer esto?

Buscando a través de stackoverflow, me encontré con esta pregunta: Why doesn’t delete set the pointer to NULL? que tenía una respuesta que hace referencia a Bjarne Stroustrup's response que incluye la declaración:

C++ permite explícitamente una implementación de borrado a cero fuera de un operando lvalue, y tuve esperaba que las implementaciones hicieran eso, pero esa idea no parece haberse vuelto popular entre los implementadores.

He leído y la sección 5.3.5 y 12.5 de la final committee draft C++0x standard re-leído, pero no estoy viendo la parte "explícita". ¿Acabo de buscar en las secciones incorrectas del estándar? O hay una cadena de lógica que está en las secciones, pero simplemente no me estoy conectando correctamente.

Ya no tengo mi copia del Manual de referencia anotado en C++. ¿Estaba en el ARM que un compilador podría hacer esto?

[Editar: Corregir la referencia de sección de 3.5.3 a 5.3.5. También estoy agregando una paradoja interesante como contrapunto a la afirmación de Henk de que p no está definido después de eliminar.]

Hay una paradoja interesante si p se inicializa a nulo.

Foo * p = 0; 
Foo * q = p; 
assert(p == 0); 
assert(p == q); 
::delete p; 
assert(p == q); 
assert(p == 0); 

En este caso, el comportamiento está bien documentado. Cuando delete obtiene un puntero nulo, se supone que no debe hacer nada, por lo que p no cambia.

+0

Bueno, el ARM no está aquí ni allá. Y olvidémonos de los lvalues. ¿Su pregunta "puede eliminar cambiar su argumento?". –

+0

Además, el estándar no es importante para decir lo que un compilador puede hacer, solo especifica lo que debe y lo que no debe hacer. No puedo ver nada en el estándar actual que prohíba cambiar el valor del puntero. –

+1

No he formulado mi pregunta como "¿Puede eliminar cambiar su argumento?" porque no quería entrar en cómo una clase que declara operador eliminar soporte cambia su argumento ya que solo toma void *, not an void * & o void **. – Ants

Respuesta

8

Puede que no sea tan explícito. En 5.3.5/7 dice que la expresión de eliminación llamará a una función de desglose. Luego en 3.7.3.2/4 dice que usar un puntero desasignado no está definido. Dado que el valor del puntero no se puede utilizar después de la desasignación, la implementación no cambia si el puntero conserva el valor o el valor lo cambia.

5.3.5/7

El borrado expresión llamará a una función de cancelación de asignación (3.7.3.2).

3.7.3.2/4

Si el argumento dado a una función de cancelación de asignación en la biblioteca estándar es un puntero que no es el valor de puntero nulo (4,10), la función de cancelación de asignación deberá cancelar la asignación del almacenamiento referenciado por el puntero, haciendo inválidos todos los punteros en referencia a cualquier parte del almacenamiento desasignado. El efecto de usar un valor de puntero no válido (que incluye pasarlo a una función de desasignación) no está definido.

Las referencias son del estándar actual. En la próxima norma 5.3.5/7 se ha reformulado:

C++ 0x FD 5.3.5/7

Si el valor del operando de la eliminación-expresión no es un valor de puntero nulo , la expresión de eliminación llamará a una función de desasignación (3.7.4.2). De lo contrario, no se especifica si se llamará a la función de desasignación. [Nota: la función de desasignación se llama independientemente de si el destructor para el objeto o algún elemento de la matriz arroja una excepción. - nota final]

+0

OK, tomo esto como apoyo para mi punto. Si no se define un puntero no válido, un compilador inteligente puede cambiar los punteros que sabe que no son válidos para que los errores se muestren mejor. Fracasar rápido, fallar temprano. –

+2

@Henk: estoy completamente de acuerdo con la intención del compilador de apoyar la filosofía de "fallar rápido, fallar temprano". Expuso un error en el código que se ha enviado durante casi 10 años. Tenemos una implementación de la lista doblemente enlazada que actualiza automáticamente los punteros siguiente, anterior y primer nodo en el destructor de un nodo. El destructor de la lista se codificó como "while (m_firstNode) delete m_firstNode;" Node destructor actualiza correctamente m_firstNode, pero el compilador sobrescribe m_firstNode después de la llamada al destructor con un valor distinto de cero. Kaboom! Si fuera explícito, el código original hubiera sido diferente. – Ants

+0

@Antes: interesante. Esto lo lleva al borde, agregando aliasing a la mezcla. –

Cuestiones relacionadas