2010-12-20 5 views
41

Supongamos que tengo una clase F que debería ser amiga de las clases G (en el espacio de nombres global) y C (en el espacio de nombres A).¿Por qué una clase de amigo de C++ necesita una declaración directa solo en otros espacios de nombres?

  • ser amigo de A::C, F debe ser declarada hacia adelante.
  • para ser amigo de G, no es necesaria la declaración directa de F.
  • del mismo modo, una clase A::BF puede ser amigo de A::C sin declaración hacia adelante

El código siguiente ilustra este y compila con GCC 4.5, VC++ otras 10 y al menos con un compilador.

class G { 
    friend class F; 
    int g; 
}; 

// without this forward declaration, F can't be friend to A::C 
class F; 

namespace A { 

class C { 
    friend class ::F; 
    friend class BF; 
    int c; 
}; 

class BF { 
public: 
    BF() { c.c = 2; } 
private: 
    C c; 
}; 

} // namespace A 

class F { 
public: 
    F() { g.g = 3; c.c = 2; } 
private: 
    G g; 
    A::C c; 
}; 

int main() 
{ 
    F f; 
} 

Para mí esto parece inconsistente. ¿Hay alguna razón para esto o solo es una decisión de diseño del estándar?

Respuesta

40

C++ estándar ISO/IEC 14882:2003(E)

7.3.1.2 definiciones de miembros Espacio de nombres

párrafo 3

Cada nombre declarado por primera vez en un espacio de nombres es un miembro de ese espacio de nombres . Si una declaración amigo en una clase no local primero declara una clase o función (esto implica que el nombre de la clase o función no está calificado) clase el amigo o función es un miembro de la más interior que encierra espacio de nombres.

// Assume f and g have not yet been defined. 
void h(int); 
template <class T> void f2(T); 
namespace A { 
    class X { 
    friend void f(X); // A::f(X) is a friend 
     class Y { 
     friend void g(); // A::g is a friend 
     friend void h(int); // A::h is a friend 
     // ::h not considered 
     friend void f2<>(int); // ::f2<>(int) is a friend 
     }; 
    }; 
    // A::f, A::g and A::h are not visible here 
    X x; 
    void g() { f(x); } // definition of A::g 
    void f(X) { /* ... */} // definition of A::f 
    void h(int) { /* ... */ } // definition of A::h 
    // A::f, A::g and A::h are visible here and known to be friends 
} 

Su friend class BF; es una declaración de espacio de nombres A::BF en un lugar de espacio de nombres global. Necesita la declaración previa global para evitar esta nueva declaración.

+0

+1 para la referencia estándar real. –

+9

Una persona razonable podría preguntar por qué no es suficiente escribir "' friend class :: F; '" (como se muestra en el código del OP), así explícitamente presionando 'F' en el espacio de nombres global. Creo que la respuesta es la siguiente: "un id calificado nunca declara un nombre nuevo", pero no estoy seguro de lo que dice el estándar en este punto. – zwol

+0

@Zack Esa fue mi pregunta inicial, también: ¿Por qué tengo que agregar una declaración directa, cuando muevo una clase en otro espacio de nombres? Pero ya hay un par de discusiones sobre este tema (por ejemplo, http://stackoverflow.com/questions/2059665/, http://stackoverflow.com/questions/1368642/). Parece que no necesitar una declaración directa en el mismo ámbito es una conveniencia que ha sido otorgada por los autores del estándar. – pesche

1

Porque no tendría sentido poder declarar algo en el espacio de nombres global si se encuentra dentro de un bloque namespace {}. El motivo por el cual friend class BF; funciona es que actúa como una declaración directa implícita.

3

Tomemos en cuenta estas 3 líneas de código a partir de su muestra:

estándar
1. friend class F; // it creates "friend declaration", (that's not the same as ordinary forward declaration 

2. class F; // without this forward declaration, F can't be friend to A::C <-- this is ordinary forward declaration 

3. friend class ::F; // this is qualified lookup (because of ::), so it needs previous declaration, which you provide in line 2. 

C++ en el apartado 7.3.1.2, punto 3 (definiciones de miembros Espacio de nombres) dice:

El amigo la declaración por sí sola no hace que el nombre sea visible para búsqueda no calificada (3.4.1) o búsqueda calificada (3.4.3).[Nota: El nombre del amigo será visible en su espacio de nombres si se proporciona una declaración correspondiente en el ámbito del espacio de nombres (antes o después de la definición de clase que otorga amistad). -finalizar nota]

Y la línea 2 sigue exactamente lo que requiere el estándar.

Toda la confusión se debe a que la "declaración de amigo" es débil, debe proporcionar una declaración directa sólida para su uso posterior.

Cuestiones relacionadas