Collision d'espace de noms C ++ dans le constructeur de copie

33

J'ai le code suivant:

namespace A {
    struct Foo {
        int a;
    };
}

struct Foo {
    int b;
};

struct Bar : public A::Foo {
    Bar(Foo foo) {
        c = foo.b;
    }
    int c;
};

Les compilateurs C ++ se plaignent à "c = foo.b" car A :: Foo n'a pas de membre nommé b. Si je change le type de paramètre Bar avec :: Foo ça marche.

Ma question est de savoir quel est le rationnel derrière ce comportement (je suppose que cela a à voir avec le fait que l'héritage oblige Bar à entrer dans l'espace de noms A, mais je ne trouve aucune documentation à l'appui de cette théorie.

Vincent Le Ligeour
la source
8
Je pense que c'est la recherche dépendante de l'argument. J'ai étiqueté "langue-avocat" car je pense que vous recherchez des réponses qui font référence à la norme linguistique. Et une très bonne première question! Ça vaut la peine.
Bathsheba
Il n'entre pas dans l'espace de noms A, que vous pouvez voir si vous laissez Barhériter d'une autre structure A. Il n'y a alors aucune ambiguïté. Cela ressemble plus à l'héritage qui ajoute tout de A::Fooà, Bary compris la résolution de Fooà A::Foo. Désolé, je ne peux pas vraiment l'exprimer plus précisément.
n314159
@Bathsheba Voulez-vous dire une recherche de nom dépendant du type d'argument pour trouver des noms de fonction (ou des noms de modèles de fonction) ou des noms dépendants dans les modèles?
curiousguy

Réponses:

22

Chaque classe a son nom inséré en tant que membre. Vous pouvez donc nommerA::Foo::Foo . Cela s'appelle le nom de classe injecté.

[classe]

2 Un nom de classe est inséré dans la portée dans laquelle il est déclaré immédiatement après que le nom de classe est vu. Le nom de classe est également inséré dans la portée de la classe elle-même; c'est ce qu'on appelle le nom de classe injecté. À des fins de vérification d'accès, le nom de classe injecté est traité comme s'il s'agissait d'un nom de membre public.

[basic.lookup]

3 Le nom de classe injecté d'une classe est également considéré comme membre de cette classe aux fins de masquage et de recherche de nom.

Étant donné que la recherche de nom non qualifié du type d'argument commence dans la portée de la classe Bar, elle continuera dans la portée de sa classe de base pour y prendre en compte tout membre. Et il trouvera A::Foo::Fooun nom de type.

Si vous souhaitez utiliser le nom de type global, qualifiez-le simplement par son espace de noms (global) environnant.

Bar(::Foo foo) {
    c = foo.b;
}

Ce qui fait une recherche complète dans une portée où le nom de classe injecté n'apparaît pas.

Pour une question de "pourquoi" de suivi, voir

Conteur - Unslander Monica
la source
5
@TedLyngmo - ADL se produit avec les appels de fonction, rien de pertinent dans ces passages spécifiques.
Conteur - Unslander Monica
Oki, je lisais et je n'étais pas sûr. Merci!
Ted Lyngmo
3
Cela mène au très amusant struct Bar:: A::Foo::Foo::Foo::Foo::Foo {}; mais il y a des contextesA::Foo::Foodésigne le constructeur et donc là, vous ne pouvez pas continuer à en ajouter autant Fooque vous le souhaitez. Ceci est similaire (mais avec un mécanisme complètement différent) du fait que vous pouvez appeler une fonction de fcette façon: (************f)().
AProgrammer
@AProgrammer - En effet. Et on peut construire des exemples encore plus amusants .
Conteur - Unslander Monica
Cette réponse explique certainement le "quoi". Peut-il être amélioré pour ajouter le "pourquoi"? Comme dans, quel est le but de cette règle? Quels cas d'utilisation améliore-t-il ou rend-il possible?
davidbak
2

Pas une réponse complète, seulement du code qui montre (puisqu'il compile) qui Barn'entre pas dans le namespace A. Vous pouvez voir que lors de l'héritage A::Foo1il n'y a pas de problème d'ambiguïté Fooqui serait différent si cet héritage laisse Barentrer A.

namespace A {
    struct Foo {
        int a;
    };

    struct Foo1 {
        int a;
    };
}

struct Foo {
    int b;
};

struct Bar : public A::Foo1 {
    Bar(Foo foo) {
        c = foo.b;
    }
    int c;
};
n314159
la source