Ist dieser Code Standard-C++? (std::swap überladung als friend einer Klasse)



  • Geht darum, dass ich eine Klasse "Room" habe. Die swap-Funktion muss als friend deklariert sein. Allerdings benutzt die STL std::swap und kommt an meinen Namespace bzw. eine Swap-Funktion ohne NameSpace nicht dran. Durch ausprobieren geht es zwar unter G++4.7, c++11, aber ist es auch überall definiert?

    // ohne Namespace
    namespace std
    {
        template<> void swap(Room& rhs, Room& lhs)
        {
            swap(rhs, lhs);
        }
    }
    
    // mit Namespace
    namespace std
    {
        template<> void swap(Room& rhs, Room& lhs)
        {
            MySpace::swap(rhs, lhs);
        }
    }
    

    Der Effekt sollte klar sein, die std::swap Funktion ruft die standard-swapfunktion auf

    Wenn es definiert ist, ist der Compiler so gut, die Funktion zu inlinen? Als Inline deklarieren wird ja nicht gehen, da dies ja nur eine Templatespezialisierung ist.


  • Mod

    Das soll vermutlich MySpace::Room sein?

    Beide Varianten sollten funktionieren, sofern MySpace::swap als normale Funktion

    void swap(Room& rhs, Room& lhs);
    

    deklariert wurde. Dann wird sie dem std::swap aus diesem Grund vorgezogen werden. Das ist allerdings recht subtil, ich würde deshalb die qualifizierte Variante vorziehen.

    Ansonsten ist wie üblich die ODR zu beachten. Wenn du eine Spezialisierung deklarierst, muss das in jeder Übersetzungseinheit geschehen, die die Spezialisierung benutzt. Und da du vermutlich bei so einer Funktion auch die Funktionsdefinition im Header erledigen wirst, muss diese explizite Spezialisierung inline deklariert werden (die Deklaration einer expliziten Spezialisierung eines Funktionstemplates deklariert eine Funktion, kein Template...).



  • Japs, Room liegt in MySpace

    namespace MySpace
    {
        class Room
        {
            size_t width_;
            size_t height_;
            Floor** floor_;
    
        public:
            // ...
            friend void swap(Room&, Room&);
            // ...
        };
    }
    

    Stimmt, was ich übersehen hatte, dass std::swap inline deklariert ist, also muss ich das auch übertragen, macht Sinn.

    Danke! 👍


  • Mod

    Cyres schrieb:

    Stimmt, was ich übersehen hatte, dass std::swap inline deklariert ist, also muss ich das auch übertragen, macht Sinn.

    Wenn du damit meinst, dass die Standardbibliothek deines Compilers das primäre Template std::swap inline deklariert: das ist völlig irrelevant.

    Deine Spezialisierung muss inline deklariert werden, weil es eine Funktionsdefinition ist.



  • Ah, okay, jetzt hab ichs 😃 Besten Dank 👍



  • Cyres schrieb:

    Floor** floor_;
    

    <sarkasmus>Ich glaube, da fehlt noch ein Sternchen.</sarkasmus>
    😉



  • krümelkacker schrieb:

    Cyres schrieb:

    Floor** floor_;
    

    <sarkasmus>Ich glaube, da fehlt noch ein Sternchen.</sarkasmus>
    😉

    ogottogottogott, jetzt werden hier krümel gekackt!
    bin dann schnell mal weg *regenschirm aufspann*



  • krümelkacker schrieb:

    Cyres schrieb:

    Floor** floor_;
    

    <sarkasmus>Ich glaube, da fehlt noch ein Sternchen.</sarkasmus>
    😉

    [sarkasmus]HTML ist hier aber nicht erlaubt ;)[/sarkasmus]
    Ja, aber wenn man ein Array in ein Array packt (STL) braucht man keine Pointer und somit swap-Funktion nicht 😉



  • Guten Morgen!

    Cyres schrieb:

    Allerdings benutzt die STL std::swap und kommt an meinen Namespace bzw. eine Swap-Funktion ohne NameSpace nicht dran.

    Kannst Du mir bitte mal ein Beispiel nennen, wo das Problem auftritt? Also: wo die STL std::swap() aufruft und nicht einfach swap(), das Deine Version per ADL aufgreift?!
    Ich sehe das Problem gerade nicht... 😕


  • Mod

    Furble Wurble schrieb:

    Guten Morgen!

    Cyres schrieb:

    Allerdings benutzt die STL std::swap und kommt an meinen Namespace bzw. eine Swap-Funktion ohne NameSpace nicht dran.

    Kannst Du mir bitte mal ein Beispiel nennen, wo das Problem auftritt? Also: wo die STL std::swap() aufruft und nicht einfach swap(), das Deine Version per ADL aufgreift?!
    Ich sehe das Problem gerade nicht... 😕

    C++03 kannte noch keine Bedingungen für "Swappable", jeder Verweis auf swap bezog sich daher auf std::swap und eine Implementation hatte es zu unterlassen, ggf. eine Funktion per ADL auszuwählen.
    In C++11 ist das anders. Hier müssen durch die Standardbibliothek immer auch Nonmemberswapfunktionen per ADL berücksichtigt werden (17.6.3.2). Eine Spezialisierung in std ist dann also häufig nicht mehr erforderlich.



  • // ohne Namespace
    namespace std
    {
        template<> void swap(Room& rhs, Room& lhs)
        {
            swap(rhs, lhs);
        }
    }
    
    // mit Namespace
    namespace std
    {
        template<> void swap(Room& rhs, Room& lhs)
        {
            MySpace::swap(rhs, lhs);
        }
    }
    

    Was scheinbar noch nicht aufgefallen ist: Wenn es sich um MySpace::Room handelt, dann ist für beide definitionen entweder ein geöffneter namespace MySpace oder ein using namespace MySpace nötig. Im ersten Fall würde man MySpace::Std::swap<Room> definieren, was nicht der Absicht entspricht (und außerdem den Compiler veranlassen würde, die Spezialisierung ohne Deklaration des allgemeinen Templates MySpace::swap abzulehnen). Im zweiten Fall hätte man vor/in einen Header eine using-Direktive geschrieben, was böse ist. Also bitte MySpace::Room voll qualifizieren, ohne usings & Co.


  • Mod

    Deshalb meine Frage zuerst. Evtl. sollte der ursprüngliche Beitrag diesbzgl. nochmal edietiert werden, so ist er nämlich nicht so gut verständlich.



  • camper schrieb:

    Furble Wurble schrieb:

    Guten Morgen!

    Cyres schrieb:

    Allerdings benutzt die STL std::swap und kommt an meinen Namespace bzw. eine Swap-Funktion ohne NameSpace nicht dran.

    Kannst Du mir bitte mal ein Beispiel nennen, wo das Problem auftritt? Also: wo die STL std::swap() aufruft und nicht einfach swap(), das Deine Version per ADL aufgreift?!
    Ich sehe das Problem gerade nicht... 😕

    C++03 kannte noch keine Bedingungen für "Swappable", jeder Verweis auf swap bezog sich daher auf std::swap und eine Implementation hatte es zu unterlassen, ggf. eine Funktion per ADL auszuwählen.
    In C++11 ist das anders. Hier müssen durch die Standardbibliothek immer auch Nonmemberswapfunktionen per ADL berücksichtigt werden (17.6.3.2). Eine Spezialisierung in std ist dann also häufig nicht mehr erforderlich.

    Es gibt ja eine Überladung von swap() für MySpace::MyRoom& , mir wäre jetzt nicht bekannt, dass c++03, c++11 hin oder her, noch eine Spezialisierung in std:: vonnöten ist. (Darum auch meine Bitte ein Beispiel für das Problem dass die zusätzliche Spezialisierung löst.)

    Und warum sollte swap() mit user-defined types in C+03 nicht ADL benutzen? Mir schwant ein Missverständnis.


  • Mod

    Furble Wurble schrieb:

    Und warum sollte swap() mit user-defined types in C+03 nicht ADL benutzen? Mir schwant ein Missverständnis.

    Sofern in deinem Code ein Ausdruck der Form

    swap(foo, bar)
    

    auftritt, wird natürlich schon immer ADL eingesetzt (sofern nicht sinnloserweise eine ungeeignete swap-Deklaration beim unqualifizierten Lookup gefunden wird).
    Die Aussage ist die, dass die Algorithmen der Standardbibliothek, die selbst swap benutzen, in C++03 grundsätzlich nur das in std befindliche swap berücksichtigen dürfen.
    Die Logik dahinter ist indirekt:
    1. der alte Standard spezifiziert nicht ausdrücklich, das ADL genutzt werden soll,
    2. der alte Standard spezifiziert auch keine Beschränkungen/bestimmtes Verhalten für non-member-swap für UDTs,
    3. käme ADL zum Einsatz und würde ein Algorithmus der Standardbibliothek eine solche nicht in std befindliche Funktion aufrufen, wäre nicht mehr garantiert (wegen 2.), dass dieser Algorithmus das richtige tut.

    Meistens steht ohnehin nicht dabei, dass swap benutzt werden soll; man denke z.B. an std::sort.



  • camper schrieb:

    Die Aussage ist die, dass die Algorithmen der Standardbibliothek, die selbst swap benutzen, in C++03 grundsätzlich nur das in std befindliche swap berücksichtigen dürfen.
    Die Logik dahinter ist indirekt:
    1. der alte Standard spezifiziert nicht ausdrücklich, das ADL genutzt werden soll,
    2. der alte Standard spezifiziert auch keine Beschränkungen/bestimmtes Verhalten für non-member-swap für UDTs,
    3. käme ADL zum Einsatz und würde ein Algorithmus der Standardbibliothek eine solche nicht in std befindliche Funktion aufrufen, wäre nicht mehr garantiert (wegen 2.), dass dieser Algorithmus das richtige tut.

    Meistens steht ohnehin nicht dabei, dass swap benutzt werden soll; man denke z.B. an std::sort.

    Evtl. kann man das aus dem Standard herauslesen, weil nur für std::swap ein Effekt und die Bedingungen aufgeführt sind. Die gesamten spezialisierten swap()s verweisen dann auf std::swap.
    Eine Implementierung, die sicher spielen will, sollte also von Überladungen die Finger lassen - kann ja sein, dass swap() nicht gleich swap ist. 🙂

    Mein gcc 4.6.1 freut sich allerdings ueber die Muehe, die ich mir gemacht habe (kompiliert mit -std=c++03 ):

    #include <iterator>
    #include <algorithm>
    #include <iostream>
    #include <cstddef>
    
    namespace Furble{
      struct Wurble{
        int i;
        void swap(Wurble& x) {
          using std::swap;  // bring into scope
          swap(i, x.i);     // use whatever fits best...
          std::cout << "Furble::Wurble::swap()\n";
        }
      };
      void swap(Wurble& x, Wurble& y){ x.swap(y); }
      std::ostream& operator<<(std::ostream& os, const Wurble& w) { return os << w.i; }
    }
    
    template<typename T, std::size_t N>
    T* begin(T(&arr)[N]) { return arr; }
    
    template<typename T, std::size_t N>
    T* end(T(&arr)[N]) { return arr+N; }
    
    int main(){
      using Furble::Wurble;
      Wurble arr[] = {{1},{2},{3},{4},{5},{6},{7}};
      std::copy(begin(arr), end(arr), std::ostream_iterator<Wurble>(std::cout << "before:\t", " "));
      std::cout << '\n';
      std::reverse(begin(arr), end(arr));
      std::copy(begin(arr), end(arr), std::ostream_iterator<Wurble>(std::cout << "after:\t", " "));
      std::cout << '\n';
    }
    

  • Mod

    begin und end gibt es schon in <iterator>



  • camper schrieb:

    begin und end gibt es schon in <iterator>

    Danke - aber ich habe für C++03 getestet.


Anmelden zum Antworten