c++ - compile - llvm 3.7 0
¿Cómo llamar explícitamente a un destructor calificado con espacio de nombres? (1)
Me sorprende que el siguiente código simple no se compile (con gcc, versión 4.8.1)
#include <string>
void test()
{
std::string* p = new std::string("Destruct me");
p->std::~string();
}
Dice: error: alcance ''std'' antes de ''~'' no es un nombre de clase. Sin embargo, al leer el estándar, diría que la sintaxis dice que debería ser " postfix-expresssion -> pseudo-constructor-name ", donde pseudo-constructor-name puede ser de la forma " nested-name-specifier ~ type-name ", y el especificador de nombre anidado puede ser " identificador ::".
Dejar "std ::" lleva a la queja de que se esperaba un nombre de clase antes del paren izquierdo, y ponerlo después de la tilde de la queja de que se esperaba un nombre de clase antes de "::". Después de intentarlo, encontré que se compilará cuando se escriba p->std::string::~string();
(pero no cuando uno escribe p->std::string::~std::string();
lugar). Pero calificar al destructor con su propio nombre de tipo no es una operación neutral; Del ejemplo en 12.4: 13 del estándar (pero curiosamente no del texto normativo), recojo que esto obliga al destructor de la clase estática (base) exacta a ser llamada, en lugar de una función virtual que (la más derivada) tipo de) el objeto real señalado. Aquí no hay diferencia, pero en casos similares lo haría; ¿Por qué la sintaxis forzaría usar exclusivamente el tipo estático?
Sin embargo, con clang en lugar de gcc, incluso la variante mencionada produce un error de sintaxis. Sin embargo, los mensajes de error de clang son más divertidos, si estás de humor para este tipo de humor cuando lees mensajes de error: para p->std::string::~string();
da "esperado el nombre de la clase después de ''~'' para nombrar un destructor" (y así lo hace; uno se pregunta qué tipo de nombres de clase no nombrarían un destructor si estuvieran prefijados con una tilde), y para mi prueba inicial p->std::~string()
replica "el acceso de miembro calificado se refiere a un miembro en el espacio de nombres ''std''" (otra vez uno se pregunta qué está mal con eso; de hecho, el destructor que se llamará vive en el espacio de nombres ''std''). He intentado las 8 combinaciones razonables (std :: y / o string :: antes de la tilde, y / o std :: después de ella) y ninguna de ellas se compila con el clang.
Puedo hacerlo compilar, incluso con clang, using std::string;
. Pero lo que me parece curioso es que no puedo encontrar ninguna indicación en el estándar de que tal declaración fuera necesaria en tales casos. De hecho, no puedo encontrar nada que aborde el problema de llamar al destructor de una clase calificada para el espacio de nombres. . ¿Me estoy perdiendo algo obvio?
Como nota final, me gustaría agregar que me parece extraño que uno tenga que usar una calificación de espacio de nombres cuando llame a un destructor. Dado que se trata de un acceso de miembros desde un objeto bien especificado (aquí *p
), ¿no debería la búsqueda dependiente del argumento hacer que el espacio de nombres no sea explícitamente calificado?
En la norma, en:
§3.4.5 / 3
Si el id. No calificado es ~ nombre-tipo, el nombre-tipo se busca en el contexto de toda la expresión-postfix.
por lo tanto, parecería que ~string
debería buscarse en el contexto del std::
nombres std::
.
De hecho, considerando que una versión casera correspondiente funciona de la siguiente manera tanto en GCC como en Clang:
namespace STD {
class STRING {};
}
int main() {
STD::STRING* a = new STD::STRING();
a->~STRING();
}
Demo en vivo con clang ++ Demo en vivo con g ++
Voy a seguir adelante y decir que esto es muy probable que sea un error.
Aparentemente, dado que std::string
es realmente std::basic_string<char>
si llama:
a->~basic_string();
Demo en vivo con clang ++ Demo en vivo con g ++
entonces todo se compila bien.
Todavía me queda la idea de que esto sea un error, considerando que el siguiente ejemplo (tomado del estándar), muestra que typedef
s también debería funcionar:
struct B {
virtual ~B() { }
};
struct D : B {
~D() { }
};
D D_object;
typedef B B_alias;
B* B_ptr = &D_object;
void f() {
D_object.B::~B();
B_ptr->~B();
B_ptr->~B_alias();
B_ptr->B_alias::~B();
B_ptr->B_alias::~B_alias();
}
Esta noción, junto con §3.4.5 / 3 debe garantizar que:
p->~string();
Deberia trabajar.