Deklaration mit new



  • David_pb schrieb:

    Wenn ein Type T implizit in auto_ptr_ref konvertiert werden kann, sollte es eigentlich gehen.

    Na dann zeig mal, wie das gehen soll.



  • Hm, kann auch sein, dass das in dem Fall einfach Unkonformität von Visual Studio ist. Folgender Code wird bei mir z.B. kompiliert (VC++ 2005):

    struct foo
    {
      foo( int x ) {}
    };
    
    struct bar
    {
      explicit bar( int x )
      {
      };
    
      bar( foo c )
      {
      }
    };
    
    int main()
    {
    	bar b = 10;
    }
    

    Wie ich das auf die Schnelle aus dem Standard rauslesen konnt ist das aber wohl nicht konform. Nunja...

    Auf jeden Fall wird bei einem T a = b; immer versucht einen entsprechenden Konstruktor zu verwenden (nicht etwa den Zuweissungsoperator).



  • David_pb schrieb:

    Auf jeden Fall wird bei einem T a = b; immer versucht einen entsprechenden Konstruktor zu verwenden (nicht etwa den Zuweissungsoperator).

    Das ist richtig. Dennoch muss bei dieser Art der Initialisierung die Zuweisung möglich sein (sprich der Ausdruck "a = b" ohne Initialisierung muss ebenfalls zulässig sein). Erst dann ist die Initialisierung auch zulässig.

    Der MSVC2005 setzt das meines Wissens in der Tat falsch um.

    EDIT:
    Zum auto_ptr_ref - über diese Klasse wird im Standard so wenig angegeben, dass Experimente damit bestenfalls rein spekulativ sind 🙂



  • LordJaxom schrieb:

    Das ist richtig. Dennoch muss bei dieser Art der Initialisierung die Zuweisung möglich sein (sprich der Ausdruck "a = b" ohne Initialisierung muss ebenfalls zulässig sein). Erst dann ist die Initialisierung auch zulässig.

    Der MSVC2005 setzt das meines Wissens in der Tat falsch um.

    Der MSVC2008 ist an der Stelle ebenfalls buggy.



  • LordJaxom schrieb:

    Zum auto_ptr_ref - über diese Klasse wird im Standard so wenig angegeben, dass Experimente damit bestenfalls rein spekulativ sind 🙂

    Ja, die Infos darüber sind in der Tat sehr rar! 😉



  • Der auto_ptr ist in C++0x eh deprecated. Nicht ohne Grund! 😉



  • bladerunner10 schrieb:

    Naja, so ganz das gleiche ist es wohl nicht. Sonst
    ware dies

    std::auto_ptr<MyClass> p(new MyClass);
    

    ja da gleiche wie dies

    std::auto_ptr<MyClass> p = new MyClass;
    

    Letzteres ist aber nicht möglich, da der Konstruktor von std::auto_ptr<> ja
    'explicit' ist.

    Kannst du bitte erklären wo da der Zusammenhang zum diskutierten Thema besteht. Wenn man das überfliegt, sieht es irgendwie nach sinnlosem Murks aus, besonders die 2. Codezeile.


  • Mod

    bladerunner10 schrieb:

    Naja, so ganz das gleiche ist es wohl nicht. Sonst
    ware dies

    std::auto_ptr<MyClass> p(new MyClass);
    

    ja da gleiche wie dies

    std::auto_ptr<MyClass> p = new MyClass;
    

    Letzteres ist aber nicht möglich, da der Konstruktor von std::auto_ptr<> ja
    'explicit' ist.

    auto_ptr ist eine Klasse, int ist ein Skalar. Und dies ist einer der Stellen, an denen dieser Unterschied nicht egal ist.

    int i = 3;
    int i(3);
    

    Beide Varianten sind semantisch absolut identisch (Letzteres ist nur furchtbar weil unleserlich... man kann mit der Vereinheitlichung auch übertreiben; an vielen Stellen habe ich echte Schwierigkeiten, dies auf Anhieb von Funktionsdeklarationen zu unterscheiden. Und eine Schreibweise, die dazu führt, dass ich das Ganze eine ganze Sekunde länger betrachten muss, ist schlecht, denn das ist zu lange, wenn man Code nur überfliegen will).

    bladerunner10 schrieb:

    Naja, so ganz das gleiche ist es wohl nicht. Sonst
    ware dies

    std::auto_ptr<MyClass> p(new MyClass);
    

    ja da gleiche wie dies

    std::auto_ptr<MyClass> p = new MyClass;
    

    Letzteres ist aber nicht möglich, da der Konstruktor von std::auto_ptr<> ja
    'explicit' ist.

    Das erste ist direkte Initialisierung, das zweite Copy-Initialisierung. Und letzteres ist mit auto_ptr eben nicht möglich (gewollt, denn man will ja nicht versehentlich Zeiger in auto_ptr stecken; das sollte immer ganz bewusst erfolgen). Das ist auch nichts besonderes, für jeden anderen vernünftigen Smartpointer gilt dies ganz genauso.

    Strolch schrieb:

    Der auto_ptr ist in C++0x eh deprecated. Nicht ohne Grund! 😉

    Natürlich erfolgt so etwas nicht ohne Grund, der wurde aber hier gar nicht genannt.

    Der Standard sagt nicht viel zu auto_ptr_ref aus mehreren Gründen: einerseits handelt es sich im Prinzip um ein Implementationsdetail; andererseits hat es offensichtlich auch im Standardkomitee lange gedauert, bis die Funktionsweise von auto_ptr wirklich verstanden wurde. Fakt ist schließlich auch, das keiner der großen Compiler (die ich daraufhin mal unter die Lupe genommen habe: msvc, bcb, g++ jeweils in verschiedenen Versionen), auto_ptr tatsächlich korrekt und sicher umsetzt - die Fehler variieren allerdings zwischen den Compilern und Versionen. Umgekehrt sollte das allerdings nicht zu dem Trugschluss verleiten, auto_ptr wäre etwas gefährliches und besser zu vermeiden - dem ist trotz dieser Bugs im Allgemeinen nicht so: für die Zwecke, für die auto_ptr primär gedacht ist, gibt es gegenwärtig keine bessere Alternative. Es ist aber eben auch kein Smartpointer für alles, wie etwa shared_ptr, den man im Allgemeinen ohne Bedenken einsetzen kann (wenn man ggf. eine gewisse Ineffizienz in Kauf nimmt).



  • camper schrieb:

    Das erste ist direkte Initialisierung, das zweite Copy-Initialisierung. Und letzteres ist mit auto_ptr eben nicht möglich (gewollt, denn man will ja nicht versehentlich Zeiger in auto_ptr stecken; das sollte immer ganz bewusst erfolgen). Das ist auch nichts besonderes, für jeden anderen vernünftigen Smartpointer gilt dies ganz genauso.

    Das zweite soll eine Copy-Initialisierung sein? Eine Copy-Initialisierung geht auch bei auto_ptr. Man kann nur eben kein auto_ptr-Objekt mit irgendeinem Objekt eines anderen Typs initialisieren. Da der Konstruktor als explicit deklariert ist gibt es keine Standardumwandlung, daher ist die die zweite Codezeile einfach ein Fehler.

    Das wäre z.B. auch bei

    int i = 3.3;
    

    oder

    std::string s = "Hallo!";
    

    oder jeder beliebigen anderen Klasse der Fall, wenn es keine entsprechende Standardumwandlung in das benötigte Format gäbe.


  • Mod

    Kritiker schrieb:

    camper schrieb:

    Das erste ist direkte Initialisierung, das zweite Copy-Initialisierung. Und letzteres ist mit auto_ptr eben nicht möglich (gewollt, denn man will ja nicht versehentlich Zeiger in auto_ptr stecken; das sollte immer ganz bewusst erfolgen). Das ist auch nichts besonderes, für jeden anderen vernünftigen Smartpointer gilt dies ganz genauso.

    Das zweite soll eine Copy-Initialisierung sein? Eine Copy-Initialisierung geht auch bei auto_ptr. Man kann nur eben kein auto_ptr-Objekt mit irgendeinem Objekt eines anderen Typs initialisieren.

    Schön, dass einer aufpasst. Ich hätte schreiben sollen: /* Edit: etwas anderes, hab grad keine Lust, über die genaue Formulierung nachzudenken */.
    Edit 2: Deine Formulierung ist ja auch nicht ganz korrekt. Man kann ja auto_ptr implizit aus anderen (verwandten) auto_ptr (unter bestimmten Umständen) initialisieren.

    struct B {}; struct D : B {};
    auto_ptr<D> p(new D);
    auto_ptr<B> q = p; // ok
    

    Entscheidend für die Korrektheit beider Formulierung ist, dass wir genauer spezifizieren, um welche Quelltypen es gehen soll.

    Die genaue Sprachregelung ist ohnehin insignifikant, wenn das Prinzip verstanden wurde. In problematischen Fällen kann es nützlich sein, verschiedene äquivalente Formulierungen zu kennen. Um etwa zu erklären warum in

    struct B {}; struct D : B {};
    auto_ptr<D> D_src();
    auto_ptr<B> p1(D_src()); //(1)
    auto_ptr<D> p1=D_src(); //(2)
    

    (1), aber nicht (2) möglich ist. Es existiert ja keine Konvertierungssequenz von rvalue-auto_ptr<D> in lvalue-auto_ptr<B>. Das ist im Grunde der einzige prinzipiell (wenn wir nicht Compiler-Magie unterstellen wollen) unheilbare Fehler im auto_ptr-Design.

    Kritiker schrieb:

    Da der Konstruktor als explicit deklariert ist gibt es keine Standardumwandlung, daher ist die die zweite Codezeile einfach ein Fehler.

    Wenn wir ganz genau sein wollen, muss es implizite Umwandlung heißen (die dann nicht möglich ist). Eine Standardumwandlung beinhaltet ohnehin nie den Aufruf eines Konstruktors oder Umwandlungsoperators.

    Das wäre z.B. auch bei

    int i = 3.3;
    

    oder

    std::string s = "Hallo!";
    

    oder jeder beliebigen anderen Klasse der Fall, wenn es keine entsprechende Standardumwandlung in das benötigte Format gäbe.

    Ganz recht (implizite Umwandlung im zweiten Fall).



  • Natürlich, es heißt "implizite Umwandlung". War "etwas" schlampig von mir. 🙂

    Stecke jetzt nicht so tief im Thema drin, aber bist du dir sicher, dass der Fall (2) in deinem Beispiel nicht funktioniert? Sieht eigentlich plausibel aus.


  • Mod

    Kritiker schrieb:

    Natürlich, es heißt "implizite Umwandlung". War "etwas" schlampig von mir. 🙂

    Stecke jetzt nicht so tief im Thema drin, aber bist du dir sicher, dass der Fall (2) in deinem Beispiel nicht funktioniert? Sieht eigentlich plausibel aus.

    Plausibel sieht es aus, und es wäre schön, wenn es funktionierte (um nackte Zeiger besser zu imitieren).

    struct B {}; struct D : B {};
    auto_ptr<B> B_src();
    auto_ptr<D> D_src();
    auto_ptr<B> p(B_src()); //(1) ok, direkte Initialisierung
    auto_ptr<B> q(D_src()); //(2) ok, direkte Initialisierung
    auto_ptr<B> r=B_src();  //(3) ok, Copy-Initialisierung vom gleichen Typ
    auto_ptr<B> s=D_src();  //(4) Fehler, Copy-Initialisierung von nicht verwandtem Typ, bräuchte Konvertierungssequenz mit 2 Konvertierungsfunktionen
    

    (1) ist Problemlos: es werden alle Konstruktoren untersucht, und nur
    auto_ptr<B>::auto_ptr(auto_ptr_ref<B>) kommt in Frage: dazu muss der initialisierende Ausdruck in ein auto_ptr_ref<B> konvertiert werden, wozu der entsprechende Konvertierungsoperator auto_ptr<B>::operator auto_ptr_ref<B> benutzt wird.
    (2) ist ebenso Problemlos, der gleiche Konstruktor wird benutzt, nur der aufgerufene Konvertierungsoperator ist hier auto_ptr<D>::operator auto_ptr_ref<B>
    (3) ist analog zu (1) weil der initialisierende Ausdruck vom gleichen Typ ist (cv-Qualifizierung ist egal und auch ein abgeleiteter Typ des Zieltyps fiele unter diese Regel)
    (4) wird anders behandelt: hier muss der initialisierende Ausdruck in auto_ptr<B> konvertiert werden, aber ein temporäres Objekt (bzw. rvalue) entsteht nur durch Konstruktoraufruf (d.h. Konvertierungsoperatoren werden nur berücksichtigt, wenn sie in cv Base& bzw. cv Derived& konvertieren - deshalb ist der Konvertierungsoperator auto_ptr<T>::operator auto_ptr<U> nutzlos und wird niemals aufgerufen). Das ist anders als die anderen Fälle, dort war der Zieltyp der Konvertierung jeweils auto_ptr_ref<B> und das temporäre Objekt entstand durch eine Operatorfunktion. Hier bräuchten wir nun 2 Funktionsaufrufe für die Konvertierung (erst den Konvertierungsoperator auto_ptr_ref, dann den Konstruktor) und das ist unzulässig. All die anderen Konstruktoren kommen nicht in Frage, weil die nur mit modifizierbaren Referenzen arbeiten und demzufolge nicht an das rvalue-Argument binden können.



  • OK, jetzt weiß ich was du meinst. War nur leicht verwirrt, weil der Quelltext in deinem Beitrag so aussah:

    camper schrieb:

    struct B {}; struct D : B {};
    auto_ptr<D> D_src();
    auto_ptr<B> p1(D_src()); //(1)
    auto_ptr<D> p1=D_src(); //(2)
    

    Du meintest hier wohl im Fall (2) auto_ptr<B>.

    camper schrieb:

    Edit 2: Deine Formulierung ist ja auch nicht ganz korrekt. Man kann ja auto_ptr implizit aus anderen (verwandten) auto_ptr (unter bestimmten Umständen) initialisieren.

    struct B {}; struct D : B {};
    auto_ptr<D> p(new D);
    auto_ptr<B> q = p; // ok
    

    Da bin ich mir auch nicht sicher ob das OK ist. Es entspricht ja mehr oder weniger deinem Fall (2).

    Noch verwirrender ist, dass z.B. MVSC++ auch die Variante (2) "schluckt" und korrekt ausführt. Während z.B. MinGW genau den von dir angedeuteten Fehler aufzeigt (no matching function call for ... ).

    Hab kurz jeweils die Headerdateien (memory) überflogen um Unterschiede zu erkennen, was mir nicht gelungen ist. Vielleicht hilft jemand aus, der das schon weiß?



  • Vermutlich wird das ganz von Mvc++ einfach falsch interpretiert.


  • Mod

    Kritiker schrieb:

    OK, jetzt weiß ich was du meinst. War nur leicht verwirrt, weil der Quelltext in deinem Beitrag so aussah:

    camper schrieb:

    struct B {}; struct D : B {};
    auto_ptr<D> D_src();
    auto_ptr<B> p1(D_src()); //(1)
    auto_ptr<D> p1=D_src(); //(2)
    

    Du meintest hier wohl im Fall (2) auto_ptr<B>.

    *kopf gegen wand* ja, genau - schon wieder nicht richtig hingeschaut.

    Kritiker schrieb:

    camper schrieb:

    Edit 2: Deine Formulierung ist ja auch nicht ganz korrekt. Man kann ja auto_ptr implizit aus anderen (verwandten) auto_ptr (unter bestimmten Umständen) initialisieren.

    struct B {}; struct D : B {};
    auto_ptr<D> p(new D);
    auto_ptr<B> q = p; // ok
    

    Da bin ich mir auch nicht sicher ob das OK ist. Es entspricht ja mehr oder weniger deinem Fall (2).

    Keineswegs. Hier ist der initialisierende Ausdruck ein modifizierbares lvalue, dafür existiert ein entsprechender Konvertierungskonstruktor:

    template<class X>
    template<class Y> auto_ptr<X>::auto_ptr(auto_ptr<Y>&) throw();
    

    mit X=B und Y=D
    Der ist aber im Falle eines rvalues nicht nutzbar (außer man benutzt Compilererweiterungen, die rvalues auch an modifizierbare Referenzen binden, kann man im Übrigen auch bei msvc abstellen). Für rvalues bräuchten wir eine Referenz auf const (wollen wir nicht, dann geben wir const-Korrektheit auf) oder eben einen Umwandlungsoperator. Den Umwandlungsoperator haben wir zwar, nur wird der wegen der Regeln bzgl. Copy-Initialisierung, nie aufgerufen.
    Dies ist eben einer der klassischen Fälle, der die Einführung von rvalue-Referenzen mit motiviert hat (allgemein Move-Semantik); der andere wichtige Grund dort ist das Forwarding-Problem.



  • Die Argumentation scheint schlüssig, dennoch hab ich das Problem, dass Microsoft beide Varianten schluckt (Lässt sich dann mittels den von dir erwähnten Compilererweiterungen erklären. P.S. weißt du zufällig wie man die abstellt?) und z.B. MinGW aber auch g++ unter Debian keine der beiden Varianten kompilieren kann.

    Daher eine Zwischenfrage:

    Kannst du bei dir den folgenden Code tatsächlich kompilieren?

    struct B {}; struct D : B {};
    auto_ptr<D> p(new D);
    auto_ptr<B> q = p; // ok
    

  • Mod

    Kritiker schrieb:

    Kannst du bei dir den folgenden Code tatsächlich kompilieren?

    struct B {}; struct D : B {};
    auto_ptr<D> p(new D);
    auto_ptr<B> q = p; // ok
    

    Nein. Und ich habe das Gefühl, immer noch nicht ganz verstanden zu haben, wie auto_ptr funktioniert, bzw. warum es das nicht tut. Kommando zurück hinsichtlich ders Umwandlungsoperators auto_ptr<T> - der wird schon berücksichtigt (und wenn ich nicht noch mal - zu oberflächlich - im Standard nachgelesen hätte, hätte ich diesen Mist auch nicht zusammen geschrieben). Grundsätzlich müssen wir einfach alle denkbaren Konstallationen durch gehen. Die Initialisierung kann direkt oder als Copy-Initialisierung erfolgen. Der initialisierende Ausdruck kann ein l- oder rvalue sein. Schlielich kann es sich um einen verwandten Typ handeln oder etwas anderes. Zudem kann der der Ausdruck const-qualifiziert sein oder nicht. Macht insgesamt 16 Kombinationen; idealerweise sollten alle Kombinationen mit modifizierberem Initialiserer funktionieren, alle mit const sollte der Compiler zurückweisen.

    struct B {}; struct D : B {};
    typedef auto_ptr<B> aB;
    typedef auto_ptr<D> aD;
    aB& laB();
    aB raB();
    const aB& claB();
    const aB craB();
    aD& laD();
    aD raD();
    const aD& claD();
    const aD craD();
    // Bei direkter Initialisierung oder Copy-Initialisierung von gleichem oder abgeleitetem Typ:
    // untersuche alle Konstruktoren des Zieltyps und wähle den besten aus (Überladungsauflösung)
    // sonst (Copy-Initialisierung aus anderen Typen):
    // Konvertiere den Initialisierer in den Zieltypen und benutze das Ergebnis zur
    // direkten Initialisierung des Objektes
    aB p1(laB()); // direkt; auto_ptr<B>::auto_ptr(auto_ptr&) oder
                  //         auto_ptr<B>::auto_ptr(auto_ptr_ref<B>)
                  // der copy-ctor ist die bessere Überladung..ok
    aB p2=laB();  // copy;   wie p1
    aB p3(raB()); // direkt; auto_ptr<B>::auto_ptr(auto_ptr_ref<B>)
                  //   erfordert Umwandlung in auto_ptr_ref<B> über die Operatorfunktion..ok
    aB p4=raB();  // copy(gleicher Typ);   wie p3
    aB p5(laD()); // direkt; auto_ptr<B>::auto_ptr(auto_ptr<D>&) oder
                  //         auto_ptr<B>::auto_ptr(auto_ptr_ref<B>)
                  // die erste Variante ist die bessere Überladung..ok
    aB p6=laD();  // copy - Konvertierung nach aB
                  //     1. user-defined conversion mit auto_ptr<B>::auto_ptr(auto_ptr<D>&);
                  //     2. user-defined conversion mit auto_ptr<D>::operator auto_ptr<B>
                  // mehrdeutig - die Frage ob und wie das Ergebnis weiterverwendet werden kann, ist für die Überladungsauflösung hier unerheblich
                  // eine user-defined conversion sequence ist nur dann besser als eine andere, wenn beide die gleiche Funktion bzw. den gleichen
                  // Konstruktor benutzen und die Standardkonvertierungssequenz nach dem Aufruf der Funktion bzw. des Konstruktors besser ist
                  // Beides trifft hier nicht zu: die Funktionen sind unterschiedlich und die anschließende Sequenz ist jeweils die der identischen Konvertierung, also gleich
    aB p7(raD()); // direkt; auto_ptr<B>::auto_ptr(auto_ptr_ref<B>)..ok
    aB p8=raD();  // copy - Konvertierung nach aB
                  //    einzig möglich: per auto_ptr<D>::operator auto_ptr<B>
                  // das Rgebnis wird zur direkten Initialisierung (analog p3) benutzt..ok
    aB p9(claB()); // kein Kandidat..ok
    aB p10=claB(); // kein Kandidat..ok
    aB p11(craB());// kein Kandidat..ok
    aB p12=craB(); // kein Kandidat..ok
    aB p13(claD());// kein Kandidat..ok
    aB p14=claD(); // kein Kandidat..ok
    aB p15(craD());// kein Kandidat..ok
    aB p16=craD(); // kein Kandidat..ok
    

    So gesehen ist der der Templatekonstruktor, der bei p6 im Weg ist, eigentlich überflüssig, für die Initialisierung von p5 ist er nicht zwingend erforderlich.
    Aber wahrscheinlich ist bloß wieder ein Denkfehler drin. Den Zuweisungsoperator müsste man zusätzlich untersuchen.

    Edit: Comeau akzeptiert p8 nur im relaxed mode - der Wortlaut im Standard (8.5/14) ist imo aber eindeutig genug

    The semantics of initializers are as follows. The destination type is the type of the object or reference being
    initialized and the source type is the type of the initializer expression. The source type is not defined when
    the initializer is brace-enclosed or when it is a parenthesized list of expressions.
    
    — If the destination type is a reference type, see 8.5.3.
    
    — If the destination type is an array of characters or an array of wchar_t, and the initializer is a string literal,
      see 8.5.2.
    
    — Otherwise, if the destination type is an array, see 8.5.1.
    
    — If the destination type is a (possibly cv-qualified) class type:
    
        — If the class is an aggregate (8.5.1), and the initializer is a brace-enclosed list, see 8.5.1.
    
        — If the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version
          of the source type is the same class as, or a derived class of, the class of the destination, constructors
          are considered. The applicable constructors are enumerated (13.3.1.3), and the best one is
          chosen through overload resolution (13.3). The constructor so selected is called to initialize the
          object, with the initializer expression(s) as its argument(s). If no constructor applies, or the overload
          resolution is ambiguous, the initialization is ill-formed.
    
        — Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that
          can convert from the source type to the destination type or (when a conversion function is used) to a
          derived class thereof are enumerated as described in 13.3.1.4, and the best one is chosen through
          overload resolution (13.3). If the conversion cannot be done or is ambiguous, the initialization is
          ill-formed. The function selected is called with the initializer expression as its argument; if the function
          is a constructor, the call initializes a temporary of the destination type. The result of the call
          (which is the temporary for the constructor case) is then used to direct-initialize, according to the
          rules above, the object that is the destination of the copy-initialization. In certain cases, an implementation
          is permitted to eliminate the copying inherent in this direct-initialization by constructing
          the intermediate result directly into the object being initialized; see 12.2, 12.8.
    
    — Otherwise, if the source type is a (possibly cv-qualified) class type, conversion functions are considered.
      The applicable conversion functions are enumerated (13.3.1.5), and the best one is chosen through overload
      resolution (13.3). The user-defined conversion so selected is called to convert the initializer
      expression into the object being initialized. If the conversion cannot be done or is ambiguous, the
      initialization is ill-formed.
    
    — Otherwise, the initial value of the object being initialized is the (possibly converted) value of the initializer
      expression. Standard conversions (clause 4) will be used, if necessary, to convert the initializer
      expression to the cv-unqualified version of the destination type; no user-defined conversions are considered.
      If the conversion cannot be done, the initialization is ill-formed. [Note: an expression of type
      “cv1 T” can initialize an object of type “cv2 T” independently of the cv-qualifiers cv1 and cv2.
          int a;
          const int b = a;
          int c = b;
      —end note]
    


  • Danke für deine Mühe! Es wird mich allerdings etwas Zeit kosten den Beitrag durchzuarbeiten, die ich im Moment nicht habe. 😞

    Also, Fortsetzung folgt in den nächsten Tagen. 🙂


Anmelden zum Antworten