2010-11-25 6 views
7

Cómo hacer una amigo desconocidoCómo hacer una clase no definida como amigo, y lo define más adelante

template<typename T> 
class List 
{ 
protected: 

    class a { 
     int x; 
     int y; 
    private: 
     friend class b; // <------------ Why this is not an error? 
    }; 

    template <typename U > class b { //If that is not a error this should be an error 
     int z; 
     U y; 
    }; 

    public: 
     List() { 
      a* ptr = (a *)new unsigned char[sizeof(a)]; 
     } 
}; 

int main() { 
    List<int> mylist; 
} 

Por favor, vaya a través de este enlace, tengo mis preguntas como comentarios en el código. Estoy tratando de hacer de otra clase una amiga de mi clase. Pero esa clase no se sabe a la hora de hacer amigos. ¿Cuál es la regla de C++ que lo permite? Más tarde estoy definiendo esa clase de tal manera que es incompatible con la declaración de amigo. ¿Por qué no está arrojando un error? Gracias

Respuesta

6

Si el código es inválido ! Esta es una muestra interesante de cómo las plantillas pueden cambiar el significado del código de maneras sutiles. El código siguiente es válida: dice

class List 
{ 
public: 
    class a { 
     typedef int type; 
     friend class b; // that's fine! 
    }; 

    template <typename U > class b; 
}; 

class b { 
    List::a::type an_int; // allowed to access private member 
}; 

estándar en 7.3.1.2/3

Si una declaración amigo en una clase que no es local declara por primera vez una clase o function83) la clase de amigo o la función es un miembro del espacio de nombres que lo rodea.

¿Cuándo es una "primera clase declarada"? Dice que también existe

Cuando se busca una declaración previa de una clase o una función declarada como amigo, y cuando el nombre de la clase o función amiga no es ni un nombre calificado ni una plantilla, los ámbitos fuera del alcance del espacio de nombres que lo rodea no se consideran.

La búsqueda de "clase b" se delega de 7.1.5.3/2 a 3.4.4, que a su vez delega en la búsqueda de nombres no calificados en 3.4/7. Toda la pregunta ahora es si el nombre de la plantilla "b" es visible en la clase de declaración de amigo a. Si no es así, el nombre no se encuentra y la declaración de amigo se referirá a una nueva clase declarada en el alcance global. 3.3.6/1 sobre el alcance de la misma dice

El alcance potencial de un nombre declarado en una clase consiste no sólo en la región declarativa siguiente declarador del nombre, sino también de todos los cuerpos de las funciones, los argumentos por defecto, y constructor ctor- inicializadores en esa clase (incluyendo tales cosas en clases anidadas).

Haciendo caso omiso de algunos puntos pedante que harían que esta redacción se aplica a aquí (que eran un defecto sino que están fijados en la versión C++ 0x de ese párrafo, que también hace esto más fácil de leer), esta lista hace no incluir la declaración de amigo como un área donde el nombre de la plantilla es visible.

Sin embargo,, el amigo se declaró en una clase de miembro de una plantilla de clase. Cuando se crea una instancia de la clase de miembro se aplica una búsqueda diferente - ¡la búsqueda de nombres de amigos declarados en una plantilla de clase! El estándar dice

Las clases o funciones de amigo se pueden declarar dentro de una plantilla de clase. Cuando se crea una instancia de una plantilla, los nombres de sus amigos se tratan como si la especialización se hubiera declarado explícitamente en su punto de creación de instancias.

lo tanto, el siguiente código es válido:

template<typename T> 
class List 
{ 
public: 
    class a { 
     typedef int type; 
     friend class b; // that's fine! 
    }; 

    template <typename U > class b; 
}; 

// POI 
List<int>::a x; 

Cuando eso hace que List<int>::a a ser implícitamente instanciado, el nombre a se alzó la vista hacia "// PDI", como si no hubiera habido una especialización explícita declarado. En ese caso, la plantilla List::b ya ha sido declarada, y esta búsqueda la golpeará y emitirá un error porque es una plantilla y no una clase que no pertenece a la plantilla.

+0

Parece que su análisis es correcto excepto 'El nombre del amigo no se encuentra mediante la búsqueda simple de nombres hasta que se proporciona una declaración coincidente en ese ámbito de espacio de nombres (antes o después de la declaración de clase que otorga amistad)'. Su código no se compila en Comeau, Intel C++ y MSVC++. –

+0

@Prasoon El texto que cita no tiene ningún significado en este caso. El nombre del amigo no se busca aquí (se busca la plantilla). Los compiladores no definen el Estándar, así que no discuto con los compiladores que lo acepten o lo rechacen. Que GCC y Clang acepten que es algo bueno, pero mi respuesta no depende de ellos, sino simplemente del texto estándar que cito. Tanto el frontend EDG (que utilizan Intel y Comeau) como MSVC realmente tienen más errores que este, así que eso no me sorprende). –

+0

Encontré el texto un poco difícil de analizar. Podría haberme perdido algo importante. No importa También eche un vistazo a [esta pregunta similar] (http://stackoverflow.com/questions/4282662/template-friend-a-deadly-combination) y un montón de respuestas. –

2

el código está mal formado y Comeau se rechace por dar el siguiente error

error: invalid redeclaration of type name "b" (declared at 
     line 11) 

creo que esto es un error en g ++. Intel C++ también lo rechaza. Puede corregir el código definiendo la clase B sobre A.

template <typename U > class b { 
     int z; 
     U y; 
}; 
class a { 
     int x; 
     int y; 
    private: 
     friend class b<T>; 
}; 
+0

Gracias. Pero lo interesante de su publicación es que en otros compiladores es un error de redeclaración. Por lo que yo sé, la redeclaración no es un error, ¿no debería haber dicho una declaración conflictiva de diferente tipo? – Saurabh

+1

@ user420536: No necesariamente. En MSVC++ obtengo 'List :: b ': la plantilla no de clase ya ha sido declarada como una plantilla de clase. (Error de redeclaración). Se permite que diferentes compiladores analicen el código de forma diferente y, por lo tanto, pueden emitir diferentes mensajes de error. No encuentro nada incorrecto con ese mensaje de error. ':)' –

3

// Ejecutar esto- ahora se compilará para usted

template <typename U > class b; //<----- forward declaration 

template<typename T> 
class List 
{ 
protected: 


     class a { 
     int x; 
     int y; 
     private: 
      friend class b<T>; // <------------ Add <T> 
     }; 
     template <typename U > class b { 
      int z; 
      U y; 
     }; 

     public: 
     List() { 
      a* ptr = (a *)new unsigned char[sizeof(a)]; 
     } 
}; 

int main() { 
    List<int> mylist; 

} 
Cuestiones relacionadas