2012-08-29 14 views
18

Cuando compilo el siguiente fragmento con g++typedef cambios intencionados

template<class T> 
class A 
{}; 

template<class T> 
class B 
{ 
    public: 
     typedef A<T> A; 
}; 

el compilador me dice

error: declaration of ‘typedef class A<T> B<T>::A’ 
error: changes meaning of ‘A’ from ‘class A<T>’ 

Por otro lado, si cambio el typedef a

typedef ::A<T> A; 

todo compila bien con g++. Clang ++ 3.1 no le importa de ninguna manera.

¿Por qué sucede esto? Y es el segundo estándar de comportamiento?

+1

Debe ser un nivel de advertencia que por defecto lo muestra como un error. Lo mismo que puede tener una función que falta devolver y se puede informar como un error o advertencia. En general, evitaría declarar el tipo A como A . Será confuso más adelante. – Grzegorz

+0

No sé lo que dice el estándar, pero estoy feliz de que g ++ se queje ... eso es una tontería. –

+0

Creo que no es tonto ni confuso. Me encuentro con este problema bastante a menudo. En cuanto a la advertencia de la conversión de errores, no le doy ninguna bandera a g ++, ¿qué advertencias convierte en errores por defecto? – foxcub

Respuesta

10

g ++ es correcto y se ajusta al estándar. Desde [3.3.7/1]:

Un nombre de N utilizado en una clase S se referirá a la misma declaración en su contexto y cuando se re-evaluado en el alcance completo de S. No de diagnóstico se requiere por una violación de esta regla.

Antes de que el typedef, A se refirió a la ::A, sin embargo, mediante el uso de la typedef, ahora se hace referencia a la A typedef que está prohibido. Sin embargo, desde no diagnostic is required, clang también es estándar.

jogojapan's comment explica el motivo de esta regla. Tome el siguiente cambio en su código:

template<class T> 
class A 
{}; 

template<class T> 
class B 
{ 
    public: 
     A a; // <-- What "A" is this referring to? 
     typedef  A<T>   A; 
}; 

Debido a cómo funciona ámbito de clase, A a; se convierte en ambiguo.

+0

¿Qué hay de la segunda forma? ¿Es también un error y g ++ simplemente no lo informa? – foxcub

+3

@foxcub: en la segunda forma no te refieres a 'A', te refieres a' :: A'. La segunda forma es correcta. –

+0

@foxclub: La segunda forma está bien (como explica Kevin Ballard), porque el significado de 'A' no cambia. –

1

voy a añadir a la respuesta de Jesse sobre el comportamiento aparentemente peculiar de GCC en la compilación:

typedef A<T> A; 

vs

typedef ::A<T> A; 

Esto también se aplica a la utilización de las declaraciones así de la forma:

using A = A<T>; 
using A = ::A<T>; 

Lo que parece estar sucediendo dentro de GCC, es que durante la compilación del typedef/u cantar declaración declarando B :: A, que el símbolo B :: A se convierte en un candidato válido dentro de la propia instrucción using. Es decir. cuando dice using A = A<T>; o typedef A<T> A;, GCC considera candidatos ::A y B::A válidos para A<T>.

Esto parece un comportamiento extraño porque como su pregunta implica, no espera que el nuevo alias A se convierta en un candidato válido dentro del typedef, pero como también dice Jesse, cualquier cosa declarada dentro de una clase se vuelve visible para todo lo demás dentro de la clase, y en este caso aparentemente incluso la declaración misma. Este tipo de comportamiento se puede implementar de esta manera para permitir definiciones de tipo recursivas.

La solución que encontraste es especificar para GCC con precisión a qué A te refieres dentro del typedef y luego ya no se queja.