Partage
  • Partager sur Facebook
  • Partager sur Twitter

namespace et friend operator<<

Et zut, ca ne marche pas ensemble ....

Sujet résolu
    22 février 2020 à 10:21:51

    Bonjour à tous et bon weekend,

    J'aurai besoin de votre aide sur un détail ...

    • J’essaie de mettre mes librairies (enfin mes petits dev de 3 fois rien) sous un "spacename", de manière à réduire les conflits potentiels.
    • Pour les TU, j'affiche le contenu privé des variable avec des "cout <<", je suis donc appelé a ajouter dans mes class des "friend ostream & operator<< ( ostream & ...".

    Et patatra: quand je fais les deux ensemble, ça ne compile plus ! (j'ai un peu cherché sur internet, mais j'ai pas trouvé ... en fait, je sais pas trop comment caractériser mon pb!)

    En code, simplifié à l’extrême,  ça donne : 1) ca marche!

    #include <iostream>
    #include <string>
    
    using namespace std::string_literals;
    
    class truc {
    public:
        truc (std::string const& name);
        friend std::ostream & operator<< (std::ostream & out, truc const& t);
    private:
        std::string m_name;
    };
    
    truc::truc (std::string const& name) : m_name{name}
    {
    }
    
    std::ostream & operator<< (std::ostream & out, truc const& t)
    {
        out << t.m_name;
        return out;
    }
    
    int main()
    {
        std::string hello{"Hello world! my version is "};
        std::cout << hello <<__cplusplus << std::endl;
        truc t{"ca marche"s};
        std::cout << t << std::endl;
        return 0;
    }
    

    2) Avec un "namespace" ça ne compile pas:

    #include <iostream>
    #include <string>
    
    using namespace std::string_literals;
    
    namespace My {
    class truc {
    public:
        truc (std::string const& name);
        friend std::ostream & operator<< (std::ostream & out, truc const& t);
    private:
        std::string m_name;
    };
    }
    
    My::truc::truc (std::string const& name) : m_name{name}
    {
    }
    
    std::ostream & My::operator<< (std::ostream & out, My::truc const& t)
    {
        out << t.m_name;
        return out;
    }
    
    int main()
    {
        std::string hello{"Hello world! my version is "};
        std::cout << hello <<__cplusplus << std::endl;
        My::truc t{"ca marche"s};
        std::cout << t << std::endl;
        return 0;
    }
    

    erreur:

    g++ -Wnon-virtual-dtor -Wshadow -Winit-self -Wredundant-decls -Wcast-align -Wundef -Wfloat-equal -Winline -Wunreachable-code -Wmissing-declarations -Wmissing-include-dirs -Wswitch-enum -Wswitch-default -Weffc++ -Wzero-as-null-pointer-constant -Wmain -pedantic-errors -pedantic -Wextra -Wall -fexceptions -std=c++17 -Wnon-virtual-dtor -Wshadow -Winit-self -Wredundant-decls -Wcast-align -Wundef -Wfloat-equal -Winline -Wunreachable-code -Wmissing-declarations -Wmissing-include-dirs -Wswitch-enum -Wswitch-default -Weffc++ -Wzero-as-null-pointer-constant -Wmain -pedantic-errors -pedantic -Wextra -Wall -g  -c /home/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/main.cpp -o obj/Debug/main.o
    g++  -o bin/Debug/XXXXXXXXX/Debug/main.o  -static-libstdc++ -static-libgcc -static -static-libstdc++ -static-libgcc -static  
    /home/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/main.cpp:20:16: error: ‘std::ostream& My::operator<<(std::ostream&, const My::truc&)’ has not been declared within ‘My’
       20 | std::ostream & My::operator<< (std::ostream & out, My::truc const& t)
          |                ^~
    /home/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/main.cpp:10:27: note: only here as a ‘friend’
       10 |     friend std::ostream & operator<< (std::ostream & out, truc const& t);
          |                           ^~~~~~~~
    Process terminated with status 1 (0 minute(s), 0 second(s))
    1 error(s), 0 warning(s) (0 minute(s), 0 second(s))
     
    


    3) Et quand je supprime le "My::" devant "operator<<", c'est encore pire!

     std::ostream & operator<< (std::ostream & out, My::truc const& t)

    erreur:

    /home/XXXXXXXXXXXXXXXXXXXXXXXXXXXX/main.cpp:20:16: warning: no previous declaration for ‘std::ostream& operator<<(std::ostream&, const My::truc&)’ [-Wmissing-declarations]
       20 | std::ostream & operator<< (std::ostream & out, My::truc const& t)
          |                ^~~~~~~~
    /home/XXXXXXXXXXXXXXXXXXXXXXXXXXXX/main.cpp: In function ‘std::ostream& operator<<(std::ostream&, const My::truc&)’:
    /home/XXXXXXXXXXXXXXXXXXXXXXXXXXXX/main.cpp:22:14: error: ‘std::string My::truc::m_name’ is private within this context
       22 |     out << t.m_name;
          |              ^~~~~~
    /home/XXXXXXXXXXXXXXXXXXXXXXXXXXXX/main.cpp:12:17: note: declared private here
       12 |     std::string m_name;
          |                 ^~~~~~
    /home/XXXXXXXXXXXXXXXXXXXXXXXXXXXX/main.cpp: In function ‘int main()’:
    /home/denis/DevCPP/CB/Formation/delete6/main.cpp:31:15: error: ambiguous overload for ‘operator<<’ (operand types are ‘std::ostream’ {aka ‘std::basic_ostream<char>’} and ‘My::truc’)
       31 |     std::cout << t << std::endl;
          |     ~~~~~~~~~ ^~ ~
          |          |       |
          |          |       My::truc
          |          std::ostream {aka std::basic_ostream<char>}
    /home/XXXXXXXXXXXXXXXXXXXXXXXXXXXX/main.cpp:20:16: note: candidate: ‘std::ostream& operator<<(std::ostream&, const My::truc&)’
       20 | std::ostream & operator<< (std::ostream & out, My::truc const& t)
          |                ^~~~~~~~
    /home/XXXXXXXXXXXXXXXXXXXXXXXXXXXX/main.cpp:10:27: note: candidate: ‘std::ostream& My::operator<<(std::ostream&, const My::truc&)’
       10 |     friend std::ostream & operator<< (std::ostream & out, truc const& t);
          |                           ^~~~~~~~

    Merci si vous pouvez m'aidez.

    -
    Edité par Dedeun 22 février 2020 à 10:35:23

    • Partager sur Facebook
    • Partager sur Twitter
      22 février 2020 à 18:19:54

      L'erreur est explicite: il n'y a pas operator<< dans le namespace My. Mettre en amie une déclaration ne l'ajoute pas dans la liste des prototypes à implémenter, seulement dans la liste des signatures visibles (c'est très subtil :p).

      C'est à dire qu'en la définissant avec son nom complet (My::operator...), le compilateur ne trouve pas la déclaration correspondante et balance une erreur, il faut mettre explicitement le prototype dans le namespace My. Ce comportement est différent avec une définition amie qui crée tout en un.

      Du coup, en virant My:: dans la définition, le compilateur ne voit pas de déclaration (Wmissing-declaration), operator<< n'est pas ami (seule My::operator<< l'est) et les règles d'ADL trouve 2 signatures: appel ambigu.

      • Partager sur Facebook
      • Partager sur Twitter
        22 février 2020 à 19:43:14

        Bonsoir et merci Jo_link_noir,

        Quand j'ai lu les premier mots de ta réponse, j'en ai pas cru mes yeux : Tu trouves que c'est explicite toi !

        Bon d'accord, Je ne savais pas que l'ajouter en "friend" n'était pas une déclaration complète de prototype (ce que tu dis avec des mots bien mieux que moi);

        jo_link_noir a écrit:

        ... Mettre en amie une déclaration ne l'ajoute pas dans la liste des prototypes à implémenter, seulement dans la liste des signatures visibles ...

        Alors, j'ai ajouté une déclaration avant de "l'ajouter en friend" (si on le fait après, ça change rien) et ca donne:

        #include <iostream>
        #include <string>
        
        using namespace std::string_literals;
        
        namespace My {
        
        class truc;
        
        std::ostream & operator<< (std::ostream & out, truc const& t);
        
        class truc {
        public:
            truc (std::string const& name);
            friend std::ostream & My::operator<< (std::ostream & out, truc const& t);
        private:
            std::string m_name;
        };
        }
        
        My::truc::truc (std::string const& name) : m_name{name}
        {
        }
        
        std::ostream & My::operator<< (std::ostream & out, My::truc const& t)
        {
            out << t.m_name;
            return out;
        }
        
        int main()
        {
            std::string hello{"Hello world! my version is "};
            std::cout << hello <<__cplusplus << std::endl;
            My::truc t{"ca marche"s};
            std::cout << t << std::endl;
            return 0;
        }
        

        Et là ("Cocorico !") ça marche ...

        ... Sauf que ça me pose une autre question: Comme j'ai définit My::operator<<, quand je veux l'utilisé dans le main(), pourquoi je l'appel "<<" et pas "My::<<". [Il y a sûrement pas besoin de précisé le namespace, il doit être déduit du type des objets]

        En tous cas, merci beaucoup pour l'explication.

        Cordialement

        • Partager sur Facebook
        • Partager sur Twitter
          22 février 2020 à 22:25:19

          > Alors, j'ai ajouté une déclaration avant de "l'ajouter en friend" (si on le fait après, ça change rien)

          À mon avis c'est à cause du My:: avec friend, le compilateur cherche une déclaration My::operator... et comme il ne la trouve pas, il ajoute probablement My::My::operator comme signature puisqu'on est déjà dans le namespace My.

          > pourquoi je l'appel "<<" et pas "My::<<".

          À cause de l'ADL. Grosso-modo, lorsqu'une fonction est utilisée sans spécifier le namespace, le compilateur va d'abord chercher dans le namespace du type des paramètres: std et My. Il ne trouve rien dans celui de la stl, mais comme il n'y en a un valide dans My, le compilateur utilise celui-ci. C'est pour ça que le compilateur peut utiliser std::cout << int() alors que operator<<(std::ostream, int) est dans le namespace std.

          Il y a des usages très pratique et recommandé (swap, begin, end, operateur, etc, j'ai écrit un article là-dessus), mais c'est un mécanisme qui peut devenir très problématique puisque le compilateur peut choisir d'utiliser la fonction d'un autre namespace.

          #include <iostream>
          
          namespace A
          {
            class A {};
            int foo(A const&) { return 1; }
            int bar(A const&) { return 1; }
          }
          
          namespace B
          {
            int foo(A::A const&) { return 2; }
          
            template<class T>
            int bar(T const&) { return 2; }
          
            int test1()
            {
              A::A a;
              return foo(a); // ambigu
            }
          
            int test2()
            {
              A::A a;
              return bar(a); // 1
            }
          }
          
          int main()
          {
            std::cout << B::test1() << "\n";
            std::cout << B::test2() << "\n";
          }

          -
          Edité par jo_link_noir 22 février 2020 à 22:29:19

          • Partager sur Facebook
          • Partager sur Twitter
            23 février 2020 à 13:35:59

            Bonjour et à nouveau merci pour la réponse.

            Cordialement.

            PS: Je viens de passer un peu de temps sur ton blog ... il y en a des chose à lire ! ....

            • Partager sur Facebook
            • Partager sur Twitter

            namespace et friend operator<<

            × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
            × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
            • Editeur
            • Markdown