Nur temporäre Instanzierung erlauben



  • Hello Leut,

    ich will eigentlich folgenden Code kompilieren. Dies gelingt auch mit so ziemlich jedem Compiler, lediglich "gcc-Version 4.1.2" weigert sich vehement.

    -------

    class foo
    {
    protected:
    foo() {}
    foo(const foo&) {}
    public:
    static foo make() { return foo(); }
    };

    void fooFunc(const foo &)
    {
    }

    int main()
    {
    fooFunc(foo::make());
    return 0;
    }

    -------
    test.cpp: In function »int main()«:
    test.cpp:5: Fehler: »foo::foo(const foo&)« ist geschützt
    test.cpp:16: Fehler: in diesem Zusammenhang
    -------

    Was ich eigentlich will: Es soll keine Instanzierung eines nicht-temporären foo-Objekts erlaubt sein! Obiger Code hat dies geschafft und kompiliert auf den meisten Systemen auch.

    Nebenbei bemerkt: ich habe "foo(const foo&)" bereits public gesetzt, womit der Code natürlich fehlerfrei kompiliert wird. Wenn ich debugge wird der Copy Constructor jedoch NICHT aufgerufen. Warum zum Henker motzt er, wenn er "protected" ist und niemand ihn verwendet?

    Hat jemand einen anderen Ansatz mit dem ich mein Ziel erreichen kann?

    Vielen Dank!



  • Also dein Copykonstruktor wird aufgerufen ich zeig dir gleich wo. Aber erstmal was bezweckst du mit einem temporären Objekt, also wa sheist temporär? Ein Objekt stirbt ja immer dann wenn der Scope verlassen wird, und damit ist keine echte deffinition von Temporär machbar.

    Nun zu deinem Copykonstruktor. Deine static Funktion erstellt ein foo und gibt ein foo zurück, und da wird der Copykonstruktor aufgerufen, denn du gibst nicht das erstellte foo zurück sondern ne Kopie. Das kannst du richten indem du foo& als Rückgabewert der Funktion angibst.

    Und dann noch was nebenbei da du den operator= nicht eingebaut hast und damit nicht schützen konntest wäre das kopieren deines Objekts möglich.



  • Hmhh... sieht ein bisschen danach aus als wäre es ein Bug in 4.1.2! Ich habe es gerade mit 4.3.3 getestet, da war es kein Problem.

    However, wenn jemand eine Idee hat, wie man es anders machen kann, damit es auch mit 4.1.2 läuft, bitte um Lösungen.



  • Xebov schrieb:

    Nun zu deinem Copykonstruktor. Deine static Funktion erstellt ein foo und gibt ein foo zurück, und da wird der Copykonstruktor aufgerufen, denn du gibst nicht das erstellte foo zurück sondern ne Kopie.

    Klar, aber wo liegt das Problem? "make" ist im Namespace und sollte ja auf "protected" Zugriff haben.

    Zudem optimieren die Compiler hier und es wird nicht zwingend ein Copy-Construktor aufgerufen, ich glaube das mal irgendwo bei Sutter gelesen zu haben, aber kein Streit hier. Wenn ich debugge, wird jedenfalls kein CC aufgerufen

    Xebov schrieb:

    Das kannst du richten indem du foo& als Rückgabewert der Funktion angibst.

    Wenn "foo&" der Rückgabewert wäre, würde ich Verletzungen produzieren, weil das Objekt direkt während der Rückgabe auch zerstört wird!

    Xebov schrieb:

    Und dann noch was nebenbei da du den operator= nicht eingebaut hast und damit nicht schützen konntest wäre das kopieren deines Objekts möglich.

    Die Anwendung von assignment operator würde eine vorherige Instanzierung bedingen, die so ja nirgendwo möglich ist.



  • vultur_gryphus schrieb:

    Xebov schrieb:

    Nun zu deinem Copykonstruktor. Deine static Funktion erstellt ein foo und gibt ein foo zurück, und da wird der Copykonstruktor aufgerufen, denn du gibst nicht das erstellte foo zurück sondern ne Kopie.

    Klar, aber wo liegt das Problem? "make" ist im Namespace und sollte ja auf "protected" Zugriff haben.

    Zudem optimieren die Compiler hier und es wird nicht zwingend ein Copy-Construktor aufgerufen, ich glaube das mal irgendwo bei Sutter gelesen zu haben, aber kein Streit hier. Wenn ich debugge, wird jedenfalls kein CC aufgerufen

    Das stimmt natürlich hatte ich nicht dran gedacht aber hattest ja schon geschrieben das es wohl ein Bug war.

    vultur_gryphus schrieb:

    Xebov schrieb:

    Das kannst du richten indem du foo& als Rückgabewert der Funktion angibst.

    Wenn "foo&" der Rückgabewert wäre, würde ich Verletzungen produzieren, weil das Objekt direkt während der Rückgabe auch zerstört wird!

    Ups ja mein Fehler, ich hatte dabei an ne Singleton Class gedacht, du erstellst das ja nur lokal.

    vultur_gryphus schrieb:

    Xebov schrieb:

    Und dann noch was nebenbei da du den operator= nicht eingebaut hast und damit nicht schützen konntest wäre das kopieren deines Objekts möglich.

    Die Anwendung von assignment operator würde eine vorherige Instanzierung bedingen, die so ja nirgendwo möglich ist.

    Naja Instanzieren kannst du schon und das nicht zu knapp. Deine make() Funktion wirft ja bei jedem aufruf ein neues Foo raus. Rufst du sie 10mal auf hast du 10mal verschiedenes Foo, damit sind dann auch Zuweisungen untereinander Möglich.

    Mir erschließt sich allerdings nachwievor der Sinn der Klasse nicht denn mit make() kannst du beliebig viele Objekte erstellen und auch wo immer du sie haben willst. Es gibt da ja ansich keine Begrenzung wo es erstellt werden kann und wie lange es am Leben bleibt, du machst es nur mit der bauweise unmöglich das Objekt irgendwie rauszuholen aus dem Scope, aber das war dann auch schon alles. vAnsonsten erinenrt mich die Bauweise sehr stark an eine Singleton Class.

    Sorry wegen den fehlern im ersten Post man solte echt nicht total Müde anfangen zu denken 😃



  • Mir erschließt sich allerdings nachwievor der Sinn der Klasse nicht denn mit make() kannst du beliebig viele Objekte erstellen und auch wo immer du sie haben willst..

    Ja, das ist richtig. Aber es soll halt nicht möglich sein ein solches Objekt nicht-temporär zu erzeugen. Die Objekte sollen lediglich benutzt werden können, um sie direkt an Funktionen zu übergeben.

    Das hat leider komplexe Hintergründe. Mein "reales foo" kann jedenfalls mit temporären Objekten erzeugt werden. Deshalb will ich nicht, dass da sowas stehen kann, wie:

    foo test;
    fooFunc(test);
    

    Problematisch wäre es nämlich mit:

    foo test( *temporäres objekt* );
    fooFunc(test);
    

    So ist es aber kein Problem:

    fooFunc(foo:make( *temporäres objekt* ));
    

    Sorry wegen den fehlern im ersten Post man solte echt nicht total Müde anfangen zu denken

    Schmarrn, kein Problem!



  • vultur_gryphus schrieb:

    Das hat leider komplexe Hintergründe. Mein "reales foo" kann jedenfalls mit temporären Objekten erzeugt werden. Deshalb will ich nicht, dass da sowas stehen kann, wie:

    foo test;
    fooFunc(test);
    

    Problematisch wäre es nämlich mit:

    foo test( *temporäres objekt* );
    fooFunc(test);
    

    So ist es aber kein Problem:

    fooFunc(foo:make( *temporäres objekt* ));
    

    OK das verstehe ich, aber was ist mit Referenzen, oder Zeigern?

    foo test&=foo::make( *test* );
    
    foo *test=&foo::make( *test* );
    

    Das würde ja trotzdem noch funktionieren, oder irre ich mich?



  • Du musst aufpassen. Die Lebenszeit eines temporären Objektes lässt sich verlängern, wenn man es an eine konstante Refernz bindet.

    {
        foo const& foo_ref = foo::make( /* ... */ );
        fooFunc(foo_ref);
    } // Hier wird das tempräre Objekt zerstört.
    

    EDIT: Die Referenz muss auf dem Stack liegen.



  • Xebov schrieb:

    OK das verstehe ich, aber was ist mit Referenzen, oder Zeigern?

    foo test&=foo::make( *test* );
    
    foo *test=&foo::make( *test* );
    

    Das würde ja trotzdem noch funktionieren, oder irre ich mich?

    Vor Zeigern hatte ich ehrlich gesagt keine Angst. Es ist klar, dass man keine Zeiger auf temporäre Objekte legt.

    Don06 schrieb:

    Du musst aufpassen. Die Lebenszeit eines temporären Objektes lässt sich verlängern, wenn man es an eine konstante Refernz bindet.

    {
        foo const& foo_ref = foo::make( /* ... */ );
        fooFunc(foo_ref);
    } // Hier wird das tempräre Objekt zerstört.
    

    EDIT: Die Referenz muss auf dem Stack liegen.

    Danke für den Hinweis! Dies ist aber kein Problem, da durch die künstliche Verlängerung auch die Lebensdauer der evtl. zusätzlich erzeugten temporären Objekte verlängert wird.

    {
        foo const& foo_ref = foo::make( foo2::make() );
        fooFunc(foo_ref);
    } // Hier wird der Destruktor von foo und foo2 aufgerufen.
    


  • vultur_gryphus schrieb:

    Vor Zeigern hatte ich ehrlich gesagt keine Angst. Es ist klar, dass man keine Zeiger auf temporäre Objekte legt.

    Naja es würde es aber Möglich machen die Objekte außerhalb von Funktionsaufrufen anzulegen. Man könnte so zB irekt in Main ein Objekt anlegen das solange lebt wie das Programm lebt ohne eine Funktion aufzurufen.



  • Xebov schrieb:

    Naja es würde es aber Möglich machen die Objekte außerhalb von Funktionsaufrufen anzulegen. Man könnte so zB irekt in Main ein Objekt anlegen das solange lebt wie das Programm lebt ohne eine Funktion aufzurufen.

    Kann ich noch nicht nachvollziehen. Beispiel?

    Tatsache ist, dass folgendes verboten ist:

    foo *test=&foo::make( *test* );
    

    Durch make wird ein temporäres Objekt erzeugt und direkt nach dieser Zeile existiert das Objekt nicht mehr.



  • vultur_gryphus schrieb:

    Kann ich noch nicht nachvollziehen. Beispiel?

    Tatsache ist, dass folgendes verboten ist:

    foo *test=&foo::make( *test* );
    

    Durch make wird ein temporäres Objekt erzeugt und direkt nach dieser Zeile existiert das Objekt nicht mehr.

    Stimmt, ich stehe heute echt total neben mir, hätte den Gedanken zuende denken sollen, Standart und Copykonstruktor haste ja dicht gemacht damit käme man ja wirklich mit nichts mehr an die Klasse ran. Das ist wirklich nicht schlecht. Aber erzähl mal für was du soetwas brauchst?



  • Ich benutze solche Foo-(Temporär)-Objekte, um Funktionsaufrufe sehr flexibel parametrisieren zu können.

    Anstatt x verschiedene Funktionen namens y zu haben (x*y verschiedene Funktionen), wird einfach mittels dem temporären foo-Objekt parametrisiert. foo wird schlicht unterschiedlich konstruiert (verschiedene "makes" - named constructor idiom) und danach an eine eindeutige Funktion übergeben.



  • Interessant das kannte ich noch nicht, wobei mir bis eben auch nicht klar war das man ne Klasse ohne weiteres soweit abschließen kann das man echt fast nichtmehr ran kommt. Wieder was gelernt 🙂



  • Gern geschehen 😉

    Was mich aber zur eigentlichen Frage zurück bringt... Gibt es einen Workaround bzw. eine andere Art wie man sowas machen kann, damit es unter GNU C 4.1 läuft?



  • vultur_gryphus schrieb:

    Gern geschehen 😉

    Was mich aber zur eigentlichen Frage zurück bringt... Gibt es einen Workaround bzw. eine andere Art wie man sowas machen kann, damit es unter GNU C 4.1 läuft?

    Nein, das kann es auch nicht, da du voraussetzt, dass ein Compiler eine optionale Optimierung durchführt und die Syntax nicht vor Anwendung der Optimierung auf Gültigkeit prüft.

    Wenn du einmal auf www.comeaucomputing.com gehst und dort den Online-Compiler mit deinem Programm fütterst erhälst du ungefähr folgende Ausgabe:

    Thank you for testing your code with Comeau C/C++!
    Tell others about http://www.comeaucomputing.com/tryitout ! 
    
    Your Comeau C/C++ test results are as follows: 
    
    Comeau C/C++ 4.3.9 (Mar 27 2007 17:24:47) for ONLINE_EVALUATION_BETA2
    Copyright 1988-2007 Comeau Computing.  All rights reserved.
    MODE:strict errors C++ noC++0x_extensions
    
    "ComeauTest.c", line 16: error: "foo::foo(const foo &)" (declared at line 5),
              required for copy that was eliminated, is inaccessible
      fooFunc(foo::make()); 
              ^
    
    1 error detected in the compilation of "ComeauTest.c".
    In strict mode, with -tused, Compile failed
    Hit the Back Button to review your code and compile options. 
    Compiled with C++0x extensions DISabled. 
    
    Don't you find "tryitout" useful? Maintaining "tryitout", keeping it up and running 24/7, etc.
    doesn't come free. It's fast and easy to send us a donation.
    
    Even better! Buy Comeau C/C++ Today ... Anything Less Is A- 
    
    Comeau C/C++ 4.3.10.1 Beta2 is now available for LINUX/Intel, Mac PowerPC, Mac Intel, 
    and Windows (including backend support of VC++ 8.0, 9.0, and now MinGW gcc 3.4.5). 
    
    Did You Know? Comeau provides C and C++ training.
    We love beginners. You'll love our insights.
    Contact us for details.
    

    Beachte auch, dass der Compiler sogar sagt, dass er die Kopie wegoptimieren wird.



  • vultur_gryphus schrieb:

    Was mich aber zur eigentlichen Frage zurück bringt... Gibt es einen Workaround bzw. eine andere Art wie man sowas machen kann, damit es unter GNU C 4.1 läuft?

    Naja was mir da nur als Idee einfällt wäre eine echte Singleton draus zu machen. Also eine einzige statische Instanz die einfach immer von deiner Funktion mit neuen Daten gefüttert und "Umgebaut" wird, wenn du da über friends arbeitest und die wichtigen Sachen versteckst wäre es für das Programm egal ob es Kopien davon gibt die durchs Programm geistern. Das ginge natürlich nur wenn du nur eine Kopie zur selben Zeit brauchst.

    Das könnte dann so aussehen:

    class foo
    {
    protected:
        foo() {}
        foo(const foo&) {}
        foo& operator=(const foo&){}
    public:
        static foo& Instance()
        {
            static foo thefoo;
            return thefoo;
        }
        static foo& make()
        {
            foo fooone&=Instance();
            modify fooone;
            return fooone; 
        }
    };
    
    void fooFunc(const foo &)
    {
    }
    
    int main()
    {
        fooFunc(foo::make());
        return 0;
    }
    

    Damit würde das ganze Funktionieren. Die frage ist halt wieweit das für dich in Frage käme.



  • Xebov schrieb:

    Naja was mir da nur als Idee einfällt wäre eine echte Singleton draus zu machen. Also eine einzige statische Instanz die einfach immer von deiner Funktion mit neuen Daten gefüttert und "Umgebaut" wird, wenn du da über friends arbeitest und die wichtigen Sachen versteckst wäre es für das Programm egal ob es Kopien davon gibt die durchs Programm geistern. Das ginge natürlich nur wenn du nur eine Kopie zur selben Zeit brauchst.

    Ja, genau daran würde es leider scheitern

    Tippgeber schrieb:

    Nein, das kann es auch nicht, da du voraussetzt, dass ein Compiler eine optionale Optimierung durchführt und die Syntax nicht vor Anwendung der Optimierung auf Gültigkeit prüft.

    Nein, das setze ich nicht voraus. Selbst wenn er nicht optimiert und den CC braucht, er darf doch? Innerhalb von "make" ist der CC ja verfügbar. Außerhalb von "make" hat der Compiler doch nur die temporäre per const ref zu übergeben.

    Ich verstehe also nicht, warum er hier meckert. Wie gesagt, die neueren GCC schlucken es ja auch!

    Tippgeber schrieb:

    "ComeauTest.c", line 16: error: "foo::foo(const foo &)" (declared at line 5),
    required for copy that was eliminated, is inaccessible
    fooFunc(foo::make());
    ^


Log in to reply