web-dev-qa-db-fra.com

Pourquoi une classe d'amis C ++ a-t-elle besoin d'une déclaration directe uniquement dans d'autres espaces de noms?

Supposons que j'ai une classe F qui devrait être amie avec les classes G (dans l'espace de noms global) et C (dans l'espace de noms A).

  • être ami avec A::C, F doit être déclaré en avant.
  • pour être l'ami de G, aucune déclaration directe de F n'est nécessaire.
  • de même, une classe A::BF peut être l'ami de A::C sans déclaration à terme

Le code suivant illustre cela et compile avec GCC 4.5, VC++ 10 et au moins avec un autre compilateur.

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;
}

Cela me semble incohérent. Y a-t-il une raison à cela ou s'agit-il simplement d'une décision de conception de la norme?

47
pesche

C++ Standard ISO/IEC 14882:2003(E)

7.3.1.2 Définitions des membres de l'espace de noms

Paragraphe 3

Chaque nom déclaré pour la première fois dans un espace de noms est un membre de cet espace de noms . Si une déclaration friend dans une classe non locale déclare d'abord une classe ou une fonction (cela implique que le nom de la classe ou de la fonction n'est pas qualifié), la classe ou la fonction friend est membre de l'espace de noms englobant le plus interne.

// 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
}

Votre friend class BF; Est une déclaration de A::BF Dans l'espace de noms A plutôt que dans l'espace de noms global. Vous avez besoin de la déclaration préalable globale pour éviter cette nouvelle déclaration.

45
Alexey Malistov

Prenons en compte ces 3 lignes de code de votre exemple:

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.

norme C++ au paragraphe 7.3.1.2, point 3 (définitions des membres de l'espace de noms) dit:

La déclaration friend ne rend pas en soi le nom visible pour la recherche non qualifiée (3.4.1) ou la recherche qualifiée (3.4.3). [Remarque: le nom de l'ami sera visible dans son espace de noms si une déclaration correspondante est fournie au niveau de l'espace de noms (avant ou après la définition de classe accordant l'amitié). —Fin note]

Et la ligne 2 suit exactement ce que la norme exige.

Toute confusion est due au fait que la "déclaration d'amis" est faible , vous devez fournir une déclaration directe solide pour une utilisation ultérieure.

6

Parce que cela n'aurait aucun sens de pouvoir déclarer quelque chose dans l'espace de noms global si vous êtes dans un namespace {} bloquer. La raison friend class BF; fonctionne, c'est qu'il agit comme une déclaration directe implicite.

2
Billy ONeal