c++ the ¿Otro error en g++/Clang?



typename c++ (1)

Por qué GCC y Clang creen que tienen razón

K , que es el nombre de clase inyectado, tiene una naturaleza dual en el alcance de K<int> . Puede usarlo sin argumentos de plantilla. Luego se refiere a K<int> (a su propio tipo).

Puede ser seguido por una lista de argumentos de plantilla también. OMI es razonable decir que debe agregarle un prefijo a la template debido a la ambigüedad del analizador con el < que sigue. Luego se refiere al tipo especificado que está determinado por los argumentos de la plantilla.

Por lo tanto, se puede tratar como una plantilla de miembro y como un tipo anidado, dependiendo de si va seguido de una lista de argumentos de plantilla. Por supuesto, K no es realmente una plantilla de miembro. Sin embargo, la naturaleza dual del nombre de clase inyectado me parece más un truco.

El estándar tiene un ejemplo que dice así:

template <class T> struct Base { };
template <class T> struct Derived: Base<int>, Base<char> {
   typename Derived::Base b; // error: ambiguous
   typename Derived::Base<double> d; // OK
};

Uno podría estar inclinado a concluir de esto que la intención es que pueda dejar la template . El estándar dice

Para que un nombre de plantilla sea explícitamente calificado por los argumentos de la plantilla, se debe saber que el nombre se refiere a una plantilla.

No puedo ver cómo esto no se aplicaría a T::K<T> . Si T es un tipo dependiente, entonces puede simplemente recostarse porque no puede saber a qué se refiere K al analizarlo, así que para darle sentido al código, solo tiene que poder prefijarlo con la template . Observe que n3225 también tiene ese ejemplo, pero no es un defecto allí: puede abandonar oficialmente la template si busca dentro del alcance de la plantilla en C ++ 0x (se denomina "instanciación actual").

Hasta ahora, Clang y GCC están bien.

Por qué Comeau tiene razón

Solo para hacerlo aún más complicado, tendremos que considerar los constructores de K<int> . Hay un constructor predeterminado y un constructor de copia implícitamente declarado. Un nombre K<int>::K se referirá al constructor (es) de K<int> menos que la búsqueda de nombre utilizada ignore los nombres de la función (constructor). Will typename T::K ignora los nombres de las funciones? 3.4.4 / 3 dice sobre especificadores de tipos elaborados, cuyo typename ... es uno de:

Si el nombre es un id calificado, el nombre se busca de acuerdo con sus calificaciones, como se describe en 3.4.3, pero ignorando los nombres que no sean de tipo que se hayan declarado.

Sin embargo, un typename ... usa una búsqueda diferente. 14.6 / 4 dice

La búsqueda habitual de nombres calificados (3.4.3) se usa para encontrar el id calificado incluso en presencia de typename.

La búsqueda habitual habitual de 3.4.3 no ignorará los nombres que no sean de tipo, como se ilustra en el ejemplo adjunto a 14.6 / 4. Por lo tanto, encontraremos los constructores como se especifica en 3.4.3.1/1a (el giro adicional que esto solo sucede cuando no se ignoran los no-tipos fue agregado por un informe de defectos posterior, que implementan todos los compiladores populares de C ++ 03) aunque):

Si el especificador de nombre anidado designa una clase C, y el nombre especificado después del especificador de nombre anidado, cuando se busca en C, es el nombre de clase inyectado de C (cláusula 9), se considera que el nombre nombre el constructor de la clase C. Tal nombre de constructor se usará solo en el identificador-declarador de una definición de constructor que aparece fuera de la definición de la clase.

Así que, al final, creo que comeau es correcto para diagnosticar esto, porque intenta poner una lista de argumentos de plantilla en una plantilla no y también viola el requisito citado en la última parte (usa el nombre en otro lugar).

Cambiémoslo accediendo al nombre inyectado por una clase derivada , para que no se produzca la traducción del nombre del constructor, y realmente acceda al tipo para que pueda anexar los argumentos de la plantilla:

// just replace struct X with this:
template<typename T>
struct X
{
   struct Derived : T { };
   typename Derived::template K<T> *p;
};

¡Todo se compila ahora con Comeau también! Fíjate que ya hice un informe del problema para hablar de esto exactamente. Ver la resolución incorrecta del nombre del constructor . Por cierto, si declaras un constructor predeterminado en K , puedes ver que comeau da un mejor mensaje de error si usas T::K<int>

"ComeauTest.c", line 13: error: overloaded function "N::K<T>::K [with T=int]" is
          not a template
     typename T::template K<T> *p;

Echa un vistazo al siguiente código (escrito solo por diversión)

namespace N
{
   template<typename T>
   struct K
   {

   };
}
template<typename T>
struct X
{
   typename T::template K<T> *p; //should give error 
                                 //N::K<int> has no template member named `K`
};

int main()
{
   X<N::K<int> > l;
}

El código se compila en g ++ (4.5.1) y Clang, mientras que Comeau e Intel C ++ dan errores (similares).

Los errores que obtengo en Comeau son:

"ComeauTest.c", line 13: error: class "N::K<int>" has no member "K"
     typename T::template K<T> *p;
                          ^
          detected during instantiation of class "X<T> [with T=N::K<int>]" at
                    line 18

"ComeauTest.c", line 13: error: expected an identifier
     typename T::template K<T> *p;
                           ^
          detected during instantiation of class "X<T> [with T=N::K<int>]" at
                    line 18

Entonces mi pregunta es "¿La muestra del código está mal formada?" Según mi "Sí". ¿Eso significa que este es otro error en g ++ / Clang?





templates