IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

ostream, opérateur << : problème avec les namespaces ?


Sujet :

Langage C++

  1. #1
    Membre éclairé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Points : 765
    Points
    765
    Par défaut ostream, opérateur << : problème avec les namespaces ?
    Bonjour,

    Je viens de tomber sur un problème qui me dépasse...
    Jusqu'à présent, j'ai eu souvent l'occasion d'introduire de nouveaux opérateurs de flux vers std::ostream, et ça ne m'a jamais posé de soucis. Mais j'ai eu besoin aujourd'hui, pour déboguer un programme, de sérialiser un std::array<float,4>.

    Pour m'éviter la corvée de tout ré-écrire à chaque fois, je me suis dit que ça ne pourrait pas faire de mal d'écrire un opérateur << template pour gérer tous les cas possibles :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    namespace utils {
        template<class T, size_t N>
        std::ostream& operator << (std::ostream& o, const std::array<T, N>& a)
        {
            // ...
        }
    }
    À ma grande surprise, je n'ai pas été capable de l'utiliser au sein de mon namespace gui (autre que celui dans lequel j'ai défini l'opérateur : utils) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    namespace gui {
        void foo()
        {
            std::array<float,4> a = {{1,2,3,4}};
            std::cout << a << std::endl;
        }
    }
    Le compilateur semble ne pas trouver l'opérateur en question :
    test.cpp: In function ‘void gui::foo()’:
    test.cpp:32:22: error: cannot bind ‘std::ostream {aka std::basic_ostream<char>}’ lvalue to ‘std::basic_ostream<char>&&’
    /usr/include/c++/4.6/ostream:581:5: error: initializing argument 1 of ‘std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char, _Traits = std::char_traits<char>, _Tp = std::array<float, 4u>]’
    Cela semble provenir d'un conflit avec un autre opérateur << que j'ai écrit au sein du namespace gui pour un type perso. En effet, voici un exemple minimal qui reproduit le problème :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    #include <iostream>
    #include <array>
     
    namespace utils {
        // L'operateur que je souhaite rajouter
        template<class T, size_t N>
        std::ostream& operator << (std::ostream& o, const std::array<T, N>& a)
        {
            o << "(";
            for (size_t i = 0; i < N; ++i)
            {
                if (i != N-1)
                    o << a[i] << ", ";
                else
                    o << a[i];
            }
            o << ")";
            return o;
        }
    }
     
    // Un namespace quelconque
    namespace gui {
        // Un nouveau type bidon, juste pour introduire un autre operateur <<
        struct color { unsigned int pack; };
     
        // ... que voici
        std::ostream& operator << (std::ostream& o, const color& c)
        {
            return o << c.pack;
        }
     
        // La fonction qui pose problème
        void foo()
        {
            std::array<float, 4> a = {{1, 2, 3, 4}};
            std::cout << a << std::endl;
        }
    }
     
    int main(int argc, char* argv[])
    {
        gui::foo();
        return 0;
    }
    Si on commente le type bidon color et son opérateur << associé, alors ça compile. Sinon, le compilateur ne trouve pas l'opérateur pour le std::array (il me sort l'erreur citée plus haut, qui apparaît également si on commente l'opérateur << pour std::array).
    Si on met l'opérateur << pour std::array dans le namespace global, ça ne change rien.

    Les seules manières que j'ai trouvées pour que le code compile sont donc :
    • ne définir aucun autre opérateur << dans le namespace gui (vraiment louche),
    • placer l'opérateur << pour std::array dans le namespace gui (pénible, il faut le faire pour chaque namespace),
    • appeler explicitement l'opérateur avec son namespace : utils::operator << (std::cout, a) (lourdingue),
    • ou enfin le définir directement dans le namespace std (ce qui permet d'utiliser le Koenig lookup, si je ne m'abuse, mais je n'aime pas trop magouiller avec le namespace std).


    Avez-vous une idée de ce qui cloche ?
    En y réfléchissant, je pense qu'il est normal que le code ne compile pas, puisqu'aucun des arguments de l'opérateur << n'appartient au namespace utils, et donc le Koenig lookup ne peut pas fonctionner. Mais alors pourquoi est-ce que ça compile s'il n'y a aucun autre opérateur de flux dans le namespace gui ?

    Je suis sous linux, et compile avec gcc 4.6.1 (avec l'option -std=c++0x, bien entendu).

  2. #2
    Invité
    Invité(e)
    Par défaut
    Bonjour,


    Le "problème" vient du mode de recherche de la surcharge appropriée en C++ (Koenig Lookup). Tu peux renseigner dessus ici et . gcc a donc exactement le comportement prévu.
    La meilleure solution serait peut-etre de définir une fonction libre print_arrayTu peux aussi definir un type range dans utils (avec les fonctions qui vont bien pour le construire) et faire un fonction générique ( prenant une paire d'itérateur) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
     
    namespace utils
    {
    template<class It>
    class range { /* ... */ };
     
    template<class Cont>
    range< typename Cont::iterator > whole( Cont& c )
    {
        return range< typename Cont::iterator>(c.begin(), c.end());
    }
     
     
    template<class Cont>
    range< typename Cont::const_iterator > whole( Cont const& c )
    {
        return range< typename Cont::const_iterator >(c.begin(), c.end());
    }
     
    template<class It>
    std::ostream& operator<< ( std::ostream& os, range<It> r )
    {
        typedef typename iterator_traits<It>::value_type Val;
     
        It before_last = r.end();
        before_last--;
     
        os << "(";
        std::copy( r.begin(), before_last, std::ostream_iterator<Val>(os, ", "));
        os << *before_last << ")";
        return os;
    }
    }
     
    // utilisation :
    std::cout << utils::whole(mon_array) << ", " << utils::whole( ma_liste );

  3. #3
    Membre éclairé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Points : 765
    Points
    765
    Par défaut
    Citation Envoyé par Joe Dralliam Voir le message
    Le "problème" vient du mode de recherche de la surcharge appropriée en C++ (Koenig Lookup).
    Je pense avoir bien compris ce mécanisme, mais peut être que c'en est une subtilité qui m'échappe. Voilà un exemple plus simple encore de ce qui me chagrine :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    #include <iostream>
    #include <array>
     
    std::ostream& operator << (std::ostream& o, const std::array<float,4>& a);
     
    namespace NS {
     
        struct color;
     
        // 1
        std::ostream& operator << (std::ostream& o, const color& c);
     
        void foo()
        {
            std::array<float, 4> a = {{1, 2, 3, 4}};
            std::cout << a << std::endl;
        }
    }
     
    int main(int argc, char* argv[])
    {
        NS::foo();
        return 0;
    }
    Ici, l'opérateur que je cherche à appeler est dans le namespace global : je dois donc toujours être capable de l'utiliser, Koenig lookup ou pas. Problème : ça ne compile que si on commente la ligne // 1. Pourquoi ?

    Sinon, je pense que ton approche est satisfaisante. Mais j'aimerais comprendre pourquoi le code ci-dessus ne compile pas

  4. #4
    Invité
    Invité(e)
    Par défaut
    Je viens de retrouver le lien que je cherchais: la section "Name hiding in nested namespace" devrait particulièrement t'interesser. http://www.gotw.ca/publications/mill08.htm

  5. #5
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    La recherche des fonctions en C++ ne prend pas en compte le fait qu'elle soit valide : le compilateur recherche déjà les fonctions dont le nom correspond, et ensuite il regarde celle qui convient le mieux.

    Dans ton dernier exemple, si tu ne commentes pas il y a deux mécanismes de recherche :
    -> Le premier part du scope du lieux de l'appel et remonte jusqu'à trouver une fonction, ici il s'arrete au début et il trouve ta fonction //1 dans le namespace NS.
    -> Le second recherche dans les namespace des arguments de l'appel, ici c'est std : il ajoute l'ensemble des fonction operator << à la liste des fonctions.

    Ensuite il regarde celles qui correspondent : il n'en trouve aucune.

    Lorsque tu commentes, les même méchanismes ont lieux, sauf que le premier méchanisme ne trouve rien dans NS donc il remonte dans le scope global et là il trouve la fonction que tu veux. Et au final cette fonction convient donc ca compile.

    Edit: Tu peux utiliser une directive using pour réimporter les symboles dont tu as besoin dans le namespace que tu veux.

  6. #6
    Membre éclairé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Points : 765
    Points
    765
    Par défaut
    Ah bien vu ! Merci pour vos réponses.
    Effectivement, en remplaçant l'opérateur par une fonction plus classique (comme dans le dernier lien de Joe), on comprend mieux ce qui se passe.

    C'est assez malvenu, je trouve... Le compilateur devrait être capable de trouver une définition qui compile, si elle existe, et ne pas s'arrêter "bêtement" à la première fonction qui a le bon nom (une fonction c'est un identifier, mais aussi et surtout une signature). Qu'est-ce qui justifie ce comportement (qui, a priori, est inscrit dans la norme) ? Ce n'est pas expliqué dans le billet de Herb Sutter.

    Quand au using, oui c'est une possibilité mais ça reste lourd à utiliser.
    Est-ce une si mauvaise idée d'ajouter cet opérateur "à la main" dans le namespace std ? C'est la solution la plus simple à mettre en place, et qui permet d'utiliser la syntaxe la plus simple, mais est-ce portable/sûr ?

  7. #7
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Ca ne me choque pas plus que ca. Quand tu écris un namespace, si tu y met une fonction avec un certain nom, alors ce nom définie une fonctionnalité (deux fonctions avec le même nom définissent la même fonctionnalité : sur des types différents, mais ca reste la même). Ainsi si on appel cette fonction au sein de ce namespace il y a une certaine logique à considérer que c'est cette fonctionnalité qui doit être utilisé et pas un d'un namespace englobant.

    Par contre si cette fonctionnalité n'existe pas au sein du namespace, voir de proche en proche si elle existe reste logique : on ne définit pas cette fonctionnalité mais peut-être que les scope englobant le font, par contre si elle existe, c'est qu'on a prévu une telle fonctionnalité, en utiliser une dans scope englobant revient à utiliser une fonctionnalité non prévue.

    A ca l'ADL vient se rajouter. Comme l'explique Sutter (j'ai pas relu l'article, donc il est possible que j'extrapole un peu), quand on définie une classe, l'ensemble des fonctions du même namespace sont des fonctionnalités associé à la classe. Ainsi ajouter les namespaces des arguments permet de prendre en compte ces diverses fonctionnalités quelque soit le lieux de l'appel.

    Si tu objectif n'est pas de changer les fonctionnalités, mais d'en rajouter une, dans ce cas une directive using convient :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    namespace A
    {
    void foo(T);
     
    namespace B
    {
    void foo(U);
    }
    }
    //Ou
    namespace A
    { void foo(T); }
     
    namespace B
    { void foo(U); }
    Dans ce code les deux fonctions sont associé à la même fonctionnalité, cependant on considère que cette fonctionnalité fonctionne différement depuis le scope B que depuis le scope A. Si par contre on considère dans le premier cas que la fonctionnalité présente dans B doit étendre celle de A et non la changer, alors tu peux utiliser une directive using :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    namespace A
    {
    void foo(T);
     
    namespace B
    {
    using A::foo;
    void foo(U);
    }
    }
    De même dans le second code, si tu considères que le scope B doit aussi fournir les fonctionnalités du scope A, alors à nouveau une directive using convient :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    namespace A
    { void foo(T); }
     
    namespace B
    { 
    using A::foo;
    void foo(U); 
    }
    AMA, la première utilisation est plus légitime que la seconde. La première pourrait même dans un sens être le comportement du C++ par défaut (mais dans ce cas il faudrait un moyen de faire "l'inverse" d'un using, je pense que c'est mieux dans ce sens que dans l'autre).

    La seconde (et c'est ton cas) est révélateur d'un ajout d'une fonctionnalité après conception. Dans ton cas tu ajoutes une fonctionnalité à std::array après que la fite classe ai été concu. Dans ce cas le using permet de dire : "J'ai besoin d'utiliser ces fonctionnalités supplémentaire non prévu". Si elles avaient été prévu elle seraient trouvées par l'ADL.

    En ce qui concerne le namespace stl, tu n'as pas le droit d'y ajouter des choses (interdit par la norme), la seul chose que tu peux faire, il me semble, c'est spécialiser les templates.

  8. #8
    Membre éclairé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Points : 765
    Points
    765
    Par défaut
    Je comprends la logique que tu exposes, mais je trouve quand même louche de cacher systématiquement les fonctions qui ont le même nom, sans considération pour la signature. Par exemple là :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void traiter(const std::string& s1, const std::string& s2);
     
    namespace NS {
        void traiter(const std::string& s1);
    }
    ça n'aurait pas de sens car la fonctionnalité est a priori différente, et pourtant la fonction traiter qui prend deux arguments n'est pas utilisable dans NS, à moins de la qualifier explicitement (::traiter), ce qu'on ne devrait pas avoir à faire étant donné qu'aucune autre fonction ne possède cette signature (i.e. le compilateur est tout à fait capable de la trouver)...

    Cette logique là, je ne la comprend pas.

    D'autant plus qu'il doit manquer quelque chose à la procédure de recherche que vous avez décrite ci-dessus. En effet, si on reprend l'exemple précédent et qu'on remplace tous les types par des types appartenant au namespace global :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    struct A {};
     
    void traiter(const A& a1, const A& a2);
     
    namespace NS {
        void traiter(const A& a);
    }
    alors les deux fonctions sont accessibles dans NS. Si on remplace :
    par :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    namespace OUT {
        struct B {};
    }
    typedef OUT::B A;
    ce n'est plus le cas. Comme on s'y attend, vu que le type concerné est dans le namespace OUT, l'ADL s'applique et rend disponibles toutes les fonctions traiter du namespace OUT dans le processus de recherche (en l’occurrence, il n'y en a pas). Mais apparemment il fait plus que ça : il cache aussi les fonctions du namespace global. Est-ce normal ?

    Ça me paraît être un comportement dangereux. Exemple : Bob développe une bibliothèque et met à disposition le type struct A;, que Alice utilise ensuite dans son programme (c'est elle qui écrit le namespace NS par exemple). Si un jour Bob décide de fourrer son type A dans un namespace pour une raison X, mais définit le typedef OUT::B A; pour assurer la compatibilité, et bien le code d'Alice risque de ne plus fonctionner, et je lui souhaite bon courage pour trouver la source du problème...

  9. #9
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Citation Envoyé par Kalith Voir le message
    Je comprends la logique que tu exposes, mais je trouve quand même louche de cacher systématiquement les fonctions qui ont le même nom, sans considération pour la signature. Par exemple là :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void traiter(const std::string& s1, const std::string& s2);
     
    namespace NS {
        void traiter(const std::string& s1);
    }
    ça n'aurait pas de sens car la fonctionnalité est a priori différente, et pourtant la fonction traiter qui prend deux arguments n'est pas utilisable dans NS, à moins de la qualifier explicitement (::traiter), ce qu'on ne devrait pas avoir à faire étant donné qu'aucune autre fonction ne possède cette signature (i.e. le compilateur est tout à fait capable de la trouver)...
    A mon avis c'est là que tu te trompes. Deux fonctions qui ont le même noms, peut importe leur scope, devraient avoir la même fonctionnalité. C'est d'ailleurs comme ca qu'on devrait choisir le nom d'une fonction : que fait la fonction ? Réponse : son nom. Le namespace lui, indique le contexte de cette fonctionnalité, et un "sous-namespace" un contexte plus précis. Avec cette vision, ca devient logique :
    -> Si je travaille dans un certain contexte alors j'utilise les fonctionnalités de ce contexte.
    -> Si je travaille dans un "sous-contexte", et qu'aucune fonctionnalité ne m'est proposé, alors je vais voir dans le contexte général si il y en a.

    Citation Envoyé par Kalith Voir le message
    D'autant plus qu'il doit manquer quelque chose à la procédure de recherche que vous avez décrite ci-dessus. En effet, si on reprend l'exemple précédent et qu'on remplace tous les types par des types appartenant au namespace global :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    struct A {};
     
    void traiter(const A& a1, const A& a2);
     
    namespace NS {
        void traiter(const A& a);
    }
    alors les deux fonctions sont accessibles dans NS. Si on remplace :
    par :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    namespace OUT {
        struct B {};
    }
    typedef OUT::B A;
    ce n'est plus le cas. Comme on s'y attend, vu que le type concerné est dans le namespace OUT, l'ADL s'applique et rend disponibles toutes les fonctions traiter du namespace OUT dans le processus de recherche (en l’occurrence, il n'y en a pas). Mais apparemment il fait plus que ça : il cache aussi les fonctions du namespace global. Est-ce normal ?
    Le paragraphe de la norme détaillant le mécanisme de recherche des identifiant fait 13 pages (45-58 section 3.4, n3242), je n'ai expliqué que les parties du mécanisme qui me semblait important (ie celles qui concernent les fonctions que je connais sans aller voir la norme).

    En l'occurence dans ce cas là il ne manque rien. Ce qui rend le namespace globale, eventuellement (*), disponible pour la recherche de la fonction "traiter" dans le scope NS c'est l'ADL :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    struct A {};
     
    void traiter(const A& a1, const A& a2);
    void traiter();
     
    namespace NS
    {
    	void traiter(const A& a);
    	void foo() { traiter(A(),A()); }
    	void bar() { traiter(); }
    }
    Le code de foo compile, celui de bar non. Le premier mécanisme pour la recherche considère uniquement (car il trouve une fonction) celle du namespace NS : traiter(const A& &), l'ADL considère le namespace des arguments :
    -> Si tu appels sans arguements : aucun namespace en plus.
    -> Si tu appels avec des A() : le namespace globale car A est dedans.

    Pour reprendre la "logique" que j'évoque ci-dessus :
    -> On cherche à utiliser la fonctionnalité "traiter"
    -> On travaille dans le contexte NS et il offre une telle fonctionnalité : on ne cherche pas à remonter dans des contextes plus généraux
    -> Si on travaille avec certains objets, alors il faut aussi tenir compte des fonctionnalités liés aux types de ces objets (c'est ce qu'explique Sutter : une classe ce n'est pas que des fonctions membres) : on prend en compte les fonctionnalités du contexte de ces objets

    Le typedef ne change rien à ce comportement, il ne fait que créer un nouveau nom (un alias) pour un type qui existe déjà, ca permet d'utiliser le type plus facilement, mais le namespace associé à la classe reste celui où elle est déclaré/définie (**).

    Citation Envoyé par Kalith Voir le message
    Ça me paraît être un comportement dangereux. Exemple : Bob développe une bibliothèque et met à disposition le type struct A;, que Alice utilise ensuite dans son programme (c'est elle qui écrit le namespace NS par exemple). Si un jour Bob décide de fourrer son type A dans un namespace pour une raison X, mais définit le typedef OUT::B A; pour assurer la compatibilité, et bien le code d'Alice risque de ne plus fonctionner, et je lui souhaite bon courage pour trouver la source du problème...
    Dans ce cas l'erreur vient de Bob :
    -> Développer quelque chose dans le namespace globale est rarement une bonne idée
    -> Et bien qu'il se soit rendu compte de son erreur, il n'y a pas que la classe a déplacer dans le namespace, mais aussi les fonctionnalités associées, ou au moins les réimporter si elles doivent aussi rester aussi dans le contexte général. On en revient à ce que dit Sutter : une classe ce n'est pas que des fonctions membres.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    namespace Out
    { struct B {}; }
     
    typedef Out::B A;
    void traiter(const A& a1, const A& a2);
     
    namespace Out
    { using ::traiter; }
    Cependant Alice aussi fait une erreur, quand on se retrouve à utiliser des composants développer directement dans le namesapce globale, on peut se demander si les composants utilisés sont de qualité.

    PS: Je ne fait qu'exposer ma vision "de la logique" des mécanismes proposés par la norme. C'est assez personnel, et je comprends qu'on puisse ne pas la partager

    PPS: Les codes compilent mais ne linkent pas, rajouter un corps vide pour ca.

    (*) Parler d'ADL sans appel de fonction est étrange.
    (**) D'autres lui sont associés : les namespaces des classes de bases entre autre (cf norme pour les détails).

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [2.x] Problème avec les namespaces !
    Par Guerr dans le forum Symfony
    Réponses: 10
    Dernier message: 27/09/2011, 15h01
  2. [JAXB] JAXB 1.0 Problème avec les namespaces
    Par ekremyilmaz dans le forum Format d'échange (XML, JSON...)
    Réponses: 0
    Dernier message: 30/05/2011, 18h04
  3. [DOM] Problème avec les namespaces
    Par clincks dans le forum Format d'échange (XML, JSON...)
    Réponses: 1
    Dernier message: 26/06/2006, 20h40
  4. Problème avec les opérateurs
    Par jules_lenoir dans le forum Langage
    Réponses: 4
    Dernier message: 26/01/2006, 16h56
  5. []Problème avec les formulaires Outlook
    Par davidinfo dans le forum Outlook
    Réponses: 6
    Dernier message: 05/12/2002, 09h59

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo