verstehe gcc Verhalten nicht



  • Hallo vieleicht kann mir das ja hier jemand erklären

    Hab folgenden Code:

    RndNumGenerator.h

    class RndNumGenerator{
    
    private:
      double lb;
      double ub;
      static unsigned seed;
    
      RndNumGenerator();
      RndNumGenerator(unsigned Seed);
    
    public:
      RndNumGenerator(double lb, double ub);
      RndNumGenerator(double lb, double ub, unsigned Seed);
    };
    

    RndNumGenerator.cpp

    #include "RndNumGenerator.h"
    
    unsigned RndNumGenerator::seed = 0;
    
    RndNumGenerator::RndNumGenerator(){ 
    }
    
    RndNumGenerator::RndNumGenerator(unsigned Seed){
      if(...){
        ...
        seed=Seed;
      }
    }
    
    RndNumGenerator::RndNumGenerator(double LB,double UB): lb(LB),ub(UB){
    
      RndNumGenerator::RndNumGenerator();
    }
    
    RndNumGenerator::RndNumGenerator(double LB,double UB,unsigned Seed) :lb(LB),ub(UB){
    
      RndNumGenerator::RndNumGenerator(Seed);   //<- Hier Error
    }
    

    und bekomme in Zeile "RndNumGenerator::RndNumGenerator(Seed);" den Fehler:

    RndNumGenerator.cpp:80: error: declaration of 'RndNumGenerator Seed' shadows a parameter
    

    Wenn ich NUR diese Zeile so abändere das "Seed" irgend einen anderen Namen hat z.B.:

    RndNumGenerator::RndNumGenerator(egal);
    

    hat gcc keine Probleme und bring es ohne Fehler zuende obwohl er "egal" eigentlich nicht kennen solte (so sagt mir jedenfalls mein Verständnis)

    noch ein paar Daten:
    gcc version 3.4.6
    Befehl: g++ -ansi -Wall -fPIC -DPIC -c RndNumGenerator.cpp

    Wenn mir jemand erklären könnte woher der erste Fehler kommt und warum gcc mit der 2 Version (sprich mit "RndNumGenerator::RndNumGenerator(egal);") keine Probleme hat fänd ich das echt super.

    Gruß
    nichtweise



  • Aus dem Code werde ich grade nicht schlau.

    Ich würde den Fehler eher zwei Zeilen höher vermuten, wenn Du einen Parameter "Seed" hast und in der selben Klasse einen Member "Seed".
    Dann überlagert der Parameter "Seed" den Member "Seed". Das würde auch erklären, warum es funktioniert, wenn Du den Parameter 'egal' nennst.
    In dem Fall dient die Warnung dazu, Dich davon abzuhalten, Seed aus der Klasse verwenden zu wollen, wenn in Wirklichkeit der Parameter "Seed" benutzt würde.
    Ich gehe davon aus, dass der hier bereitgestellte Code gekürzt ist, also untersuche Deinen Code auf mögliche "Seed"s, die überlagert werden könnten. Schau ebenfalls in Basisklassen.

    Version 2 meckerte vielliecht nicht, weil die neueren Versionen mehr Fehler oder Ungereimtheiten bemängeln.



  • Aus dem Code werde ich grade nicht schlau. (...) Ich gehe davon aus, dass der hier bereitgestellte Code gekürzt ist, also untersuche Deinen Code auf mögliche "Seed"s, die überlagert werden könnten. Schau ebenfalls in Basisklassen.

    Ja der Code ist gekürzt aber so dass er lauffähig ist und den Fehler reproduziert. Ok das if muss man noch abändern, hätte besser den code so reingestellt:

    #include "RndNumGenerator.h"
    
    unsigned RndNumGenerator::seed = 0;
    
    RndNumGenerator::RndNumGenerator(){
    }
    
    RndNumGenerator::RndNumGenerator(unsigned Seed){
      if(Seed > 3){
    
         seed=Seed;
      }
    }
    
    RndNumGenerator::RndNumGenerator(double LB,double UB): lb(LB),ub(UB){
    
      RndNumGenerator::RndNumGenerator();
    }
    
    RndNumGenerator::RndNumGenerator(double LB,double UB,unsigned Seed) :lb(LB),ub(UB){
    
      RndNumGenerator::RndNumGenerator(Seed);
    }
    

    Ich würde den Fehler eher zwei Zeilen höher vermuten, wenn Du einen Parameter "Seed" hast und in der selben Klasse einen Member "Seed".
    Dann überlagert der Parameter "Seed" den Member "Seed". Das würde auch erklären, warum es funktioniert, wenn Du den Parameter 'egal' nennst.

    Wie gesagt es ist alles da um den Fehler zu reproduzieren somit liegt es eigentlich nicht an einer doppelt Belegung und wenn würde das nicht erklären warum er nicht meckert wenn ich RndNumGenerator::RndNumGenerator(unsigned) einen nicht definierten Parameter übergebe.

    Naja hab noch was mit dem Code rumgespielt und die beiden Konstruktoren rausgeschmissen die meiner Meinung nicht nötig waren um den Fehler zu reproduzieren:

    RndNumGenerator.h

    class RndNumGenerator{
    
    private:
      double lb;
      double ub;
      static unsigned seed;
    
      RndNumGenerator(unsigned Seed);
    
    public:
      RndNumGenerator(double LB, double UB, unsigned Seed);
    };
    

    RndNumGenerator.cpp

    #include "RndNumGenerator.h"
    
    unsigned RndNumGenerator::seed = 0;
    
    RndNumGenerator::RndNumGenerator(unsigned Seed){
      if(Seed > 3){
    
        seed=Seed;
      }
    }
    
    RndNumGenerator::RndNumGenerator(double LB,double UB,unsigned Seed) :lb(LB),ub(UB){
    
      RndNumGenerator::RndNumGenerator(Seed);
    }
    

    bekam dann aber einen neuen Fehler dazu.

    RndNumGenerator.cpp: In constructor `RndNumGenerator::RndNumGenerator(double, double, unsigned int)':
    RndNumGenerator.cpp:16: Fehler: Deklaration von »RndNumGenerator Seed« überdeckt einen Parameter
    
    [b]RndNumGenerator.cpp:16: Fehler: keine passende Funktion für Aufruf von »RndNumGenerator::RndNumGenerator()«[/b]
    
    RndNumGenerator.h:1: Anmerkung: Kandidaten sind: RndNumGenerator::RndNumGenerator(const RndNumGenerator&)
    RndNumGenerator.h:11: Anmerkung:                  RndNumGenerator::RndNumGenerator(double, double, unsigned int)
    RndNumGenerator.cpp:8: Anmerkung:                  RndNumGenerator::RndNumGenerator(unsigned int)
    

    Sprich er will in zeile 16 garnicht RndNumGenerator::RndNumGenerator(unsigned) sondern RndNumGenerator::RndNumGenerator() aufrufen, nur warum????

    Gut dann machen wir ihm mal etwas deutlicher was wir wollen:

    #include "RndNumGenerator.h"
    
    unsigned RndNumGenerator::seed = 0;
    
    RndNumGenerator::RndNumGenerator(unsigned Seed){
      if(Seed > 3){
    
        seed=Seed;
      }
    }
    
    RndNumGenerator::RndNumGenerator(double LB,double UB,unsigned Seed) :lb(LB),ub(UB){
    
      RndNumGenerator::RndNumGenerator(unsigned(Seed));
    }
    

    und es klappt er ist zufrieden.Er kompiliert nicht nur zuende, nein mein Prog läuft auch so wie es soll.

    Nur verstehen, verstehen tu ich das ganze immer noch nicht. Also wenn jemand was Licht im Dunklen sieht ich wär immer noch an Erklärungen interessiert.

    an Xin: Danke schonmal für den ersten Versuch

    gruß
    nichtweise



  • Du kannst keinen Konstruktor so aufrufen, warum das der gcc zulaesst weiss ich nicht.



  • Thomas (: schrieb:

    Du kannst keinen Konstruktor so aufrufen, warum das der gcc zulaesst weiss ich nicht.

    Du scheinst verdammt recht zu haben. Dabei war ich mir sicher, dass ich auch Konstrutor explizit aufrufen kann aber das war nur in java so wie mich das internet aufgeklärt hat. Muss ich es halt anders machen.
    Dabei sah es zuletzt so schön aus was das Prog mir lieferte aber richtig kann es dann ja auch nicht wiklich gewesen sein naja schau ich mir nochmal an.

    Danke!!!

    gruß
    nichtweise



  • Thomas (: schrieb:

    Du kannst keinen Konstruktor so aufrufen, warum das der gcc zulaesst weiss ich nicht.

    Weil es legales C++ ist?
    Es wird ein temporäres Objekt durch den Konstruktor RndNumGenerator::RndNumGenerator(unsigned int) erzeugt, das anschließend gleich wieder gelöscht wird, wenn der Stackframe aufgeräumt wird.
    Wie kommst du auf die Idee, daß der g++ das verbieten sollte? Nur weil es nicht das macht, was man als Programmierer anderer Sprachen vermutet, heißt das noch nicht, daß es illegales C++ ist.



  • Dieser Thread wurde von Moderator/in rüdiger aus dem Forum Linux/Unix in das Forum C++ verschoben.

    Im Zweifelsfall bitte auch folgende Hinweise beachten:
    C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?

    Dieses Posting wurde automatisch erzeugt.



  • nichtweise schrieb:

    Thomas (: schrieb:

    Du kannst keinen Konstruktor so aufrufen, warum das der gcc zulaesst weiss ich nicht.

    Du scheinst verdammt recht zu haben. Dabei war ich mir sicher, dass ich auch Konstrutor explizit aufrufen kann aber das war nur in java so wie mich das internet aufgeklärt hat. Muss ich es halt anders machen.
    Dabei sah es zuletzt so schön aus was das Prog mir lieferte aber richtig kann es dann ja auch nicht wiklich gewesen sein naja schau ich mir nochmal an.

    Danke!!!

    gruß
    nichtweise

    Gar nicht gesehen. ^^
    Aber dann ist die Fehlermeldung ziemlich daneben, aber auch das wäre nicht so unüblich für den GCC.

    Für solche Fälle schreibe ich mir immer eine private 'Init' Funktion, in der die Teile, die die Kontruktoren gemeinsam haben, aufgeführt werden.
    Diese Funktion rufe ich dann von jedem Konstruktor auf.

    Unter C# kenne ich das ebenfalls, dass ein Konstruktor z.B. den Standardkonstruktor rufen darf.



  • Xin schrieb:

    Gar nicht gesehen. ^^
    Aber dann ist die Fehlermeldung ziemlich daneben, aber auch das wäre nicht so unüblich für den GCC.

    So daneben ist die Fehlermeldung nicht, wenn man weiß, was passiert, was vollkommen üblich für den GCC ist. Nach dem Aufruf von RndNumGenerator::RndNumGenerator(Seed) ist Seed eine lokale Instanz von RndNumGenerator. Der Aufruf ruft den Standardkonstruktor auf und ist somit äquivalent zu RndNumGenerator::RndNumGenerator(Seed)(). Mit RndNumGenerator::RndNumGenerator(Seed)(42) würde beispielsweise RndNumGenerator::RndNumGenerator(int) aufgerufen werden.
    Ob das so richtig interpretiertes C++ ist, weiß ich nicht, das müsste jemand sagen, der den Standard besser kennt, mir ist diese Syntax ebenfalls neu. Aber so verhält sich der GCC (darauf weist übrigens die Fehlermeldung selbst hin, wer herausfindet an welcher Stelle, kriegt 'nen Keks) und für dieses Verhalten ist diese Fehlermeldung absolut plausibel. Vorraussgesetzt, das ist legales C++, wie hätte die Fehlermeldung deiner Meinung nach hätte lauten sollen?

    Xin schrieb:

    Unter C# kenne ich das ebenfalls, dass ein Konstruktor z.B. den Standardkonstruktor rufen darf.

    Manchmal ist das praktisch, ja. Eine andere Möglichkeit wäre es übrigens, RndNumGenerator::RndNumGenerator(unsigned Seed, double LB = 0.0, double UB = 0.0) einzuführen, falls der konkrete Fall das erlaubt.



  • tommie-lie schrieb:

    Xin schrieb:

    Gar nicht gesehen. ^^
    Aber dann ist die Fehlermeldung ziemlich daneben, aber auch das wäre nicht so unüblich für den GCC.

    So daneben ist die Fehlermeldung nicht, wenn man weiß, was passiert, was vollkommen üblich für den GCC ist. Nach dem Aufruf von RndNumGenerator::RndNumGenerator(Seed) ist Seed eine lokale Instanz von RndNumGenerator. Der Aufruf ruft den Standardkonstruktor auf und ist somit äquivalent zu RndNumGenerator::RndNumGenerator(Seed)(). Mit RndNumGenerator::RndNumGenerator(Seed)(42) würde beispielsweise RndNumGenerator::RndNumGenerator(int) aufgerufen werden.
    Ob das so richtig interpretiertes C++ ist, weiß ich nicht, das müsste jemand sagen, der den Standard besser kennt, mir ist diese Syntax ebenfalls neu. Aber so verhält sich der GCC (darauf weist übrigens die Fehlermeldung selbst hin, wer herausfindet an welcher Stelle, kriegt 'nen Keks) und für dieses Verhalten ist diese Fehlermeldung absolut plausibel. Vorraussgesetzt, das ist legales C++, wie hätte die Fehlermeldung deiner Meinung nach hätte lauten sollen?

    Ich stimme Dir zu, Du beschreibst vermutlich exakt das Vorgehen, dass der Compiler vornimmt.

    Aber ich glaube, dass das Verhalten nicht sauber ist.

    Der Typ heißt RndNumGenerator, nicht RndNumGenerator::RndNumGenerator, entsprechend darf das eigentlich keine Variablendefinition sein.
    Da steht RndNumGenerator::RndNumGenerator, der eine Symbol RndNumGenerator im Namensraum RndNumGenerator ruft. Das ist der Konstruktor, für den es eine Überladung RndNumGenerator(int) gibt.
    Stünde dort RndNumGenerator(Seed) wäre das die zur Fehlermeldung passende Variablendefinition, der Aufruf des StandardKonstruktur und das temporäre Objekt heißt Seed.

    Es wird aber explizit der Konstruktor gerufen, welche eine statische Funktion ist und eine Funktion ruft man, in dem man eine Klammer anfügt. Die erste Klammer darf also nicht als überflüssige Klammerung des Variablennamens gedeutet werden, sondern muss als Funktionsaufruf gelten.

    Dieser Funktionsaufruf wäre hier den Regeln von C++ nicht angemessen. Entsprechend würde ich etwas erwarten wie 'illegal call of constructor RndNumGenerator( int ) in function RndNumGenerator( double, double, unsigned int )'.

    tommie-lie schrieb:

    Eine andere Möglichkeit wäre es übrigens, RndNumGenerator::RndNumGenerator(unsigned Seed, double LB = 0.0, double UB = 0.0) einzuführen, falls der konkrete Fall das erlaubt.

    Zustimmung. Ich würde fast schon auf einen

    RndNumGenerator::RndNumGenerator(unsigned Seed=DefaultSeed, double LB = DefaultLB, double UB = DefaultUB )
    

    setzen.



  • Xin schrieb:

    Ich stimme Dir zu, Du beschreibst vermutlich exakt das Vorgehen, dass der Compiler vornimmt.

    Ich habe es ausprobiert 😉 Gezieltes Raten, sozusagen.

    Der Typ heißt RndNumGenerator, nicht RndNumGenerator::RndNumGenerator, entsprechend darf das eigentlich keine Variablendefinition sein.

    Wie gesagt, ich habe keine Ahnung, ob das gültiges C++ ist. (RndNumGenerator(Seed) wäre jedenfalls keine Deklaration von Seed, sondern ein Typecast von Seed nach RndNumGenerator. Den Satz streiche ich mal, die Bedeutung von RndNumGenerator(Seed) ist kontextsensitiv und nicht immer ein C-Style Typecast). "RndNumGenerator Seed = RndNumGenerator(Blubb)" wäre eine mir bekannte Deklaration von Seed mit Initialisierung durch Blubb, die wenigstens so ähnlich aussieht.

    Dieser Funktionsaufruf wäre hier den Regeln von C++ nicht angemessen.

    Ja, die Syntax erscheint mir unlogisch und inkonsequent, was ich so von C++ nicht kenne.

    Entsprechend würde ich etwas erwarten wie 'illegal call of constructor RndNumGenerator( int ) in function RndNumGenerator( double, double, unsigned int )'.

    Naja, nicht wenn es legales C++ wäre, wovon der G++ ganz offensichtlich ausgeht. Vielleicht antwortet ja noch jemand, der genau sagen kann, was "T::T(t)" laut Standard machen sollte.

    Ich würde fast schon auf einen

    RndNumGenerator::RndNumGenerator(unsigned Seed=DefaultSeed, double LB = DefaultLB, double UB = DefaultUB )
    

    setzen.

    Nee. DefaultSeed ist böse, da statisch. Wenn kein Seed angegeben wurde, sollte die aktuelle Uhrzeit genommen werden.



  • tommie-lie schrieb:

    Xin schrieb:

    Ich stimme Dir zu, Du beschreibst vermutlich exakt das Vorgehen, dass der Compiler vornimmt.

    Ich habe es ausprobiert 😉 Gezieltes Raten, sozusagen.

    Manchmal bleibt einem beim G++ und seinen Fehlermeldungen auch nichts anderes über. 😉

    tommie-lie schrieb:

    Der Typ heißt RndNumGenerator, nicht RndNumGenerator::RndNumGenerator, entsprechend darf das eigentlich keine Variablendefinition sein.

    Wie gesagt, ich habe keine Ahnung, ob das gültiges C++ ist. RndNumGenerator(Seed) wäre jedenfalls keine Deklaration von Seed, sondern ein Typecast von Seed nach RndNumGenerator. "RndNumGenerator Seed = RndNumGenerator(Blubb)" wäre eine mir bekannte Deklaration von Seed mit Initialisierung durch Blubb, die wenigstens so ähnlich aussieht.

    Gutes Argument... Hier wird aber eben der Konstruktor direkt gerufen, also kann es auch keine Typumwandlung sein.
    Es kann also eigentlich in keinem Fall eine Variablendeklaration darstellen.

    tommie-lie schrieb:

    Dieser Funktionsaufruf wäre hier den Regeln von C++ nicht angemessen.

    Ja, die Syntax erscheint mir unlogisch und inkonsequent, was ich so von C++ nicht kenne.

    Naja, in C++ sind schon einige unangemessene Dinge drin. Beispielsweise ist = mal Zuweisung, mal Initialisierung. Dabei wäre eine Initialisierung eigentlich in Klammern zu schreiben. Das mag vielliecht logisch sein, in jedem Fall ist es jedoch inkonsequent.
    Manches daraus vererbt, manches ist einfach etwas fraglich oder später erst realisiert worden.
    Ich fand 'Design und Entwicklung von C++' dazu ganz interessant, darin beschreibt Bjarne Stroustrup, wie es eigentlich zu C++ kam, was in C++ nicht reinkam, was er gerne anders gemacht hätte oder weggelassen hat.

    tommie-lie schrieb:

    Ich würde fast schon auf einen

    RndNumGenerator::RndNumGenerator(unsigned Seed=DefaultSeed, double LB = DefaultLB, double UB = DefaultUB )
    

    setzen.

    Nee. DefaultSeed ist böse, da statisch. Wenn kein Seed angegeben wurde, sollte die aktuelle Uhrzeit genommen werden.

    DefaultSeed kann ganz einfach 0 sein, und wenn 0 übergeben wurde, nimmt man die Uhrzeit. Es geht vorrangig darum, zu erkennen, ob der Standard-Konstruktor gerufen wurde oder nicht.



  • Xin schrieb:

    Gutes Argument... Hier wird aber eben der Konstruktor direkt gerufen, also kann es auch keine Typumwandlung sein.
    Es kann also eigentlich in keinem Fall eine Variablendeklaration darstellen.

    Beachte dazu auch mein (kursives) Edit im letzten Post. Der G++ interpretiert RndNumGenerator(Seed) übrigens äquivalent zu RndNumGenerator::RndNumGenerator(Seed), also als Definition von Seed im lokalen Scope mittels default c'tor.

    Naja, in C++ sind schon einige unangemessene Dinge drin. Beispielsweise ist = mal Zuweisung, mal Initialisierung. Dabei wäre eine Initialisierung eigentlich in Klammern zu schreiben. Das mag vielliecht logisch sein, in jedem Fall ist es jedoch inkonsequent.
    Manches daraus vererbt, manches ist einfach etwas fraglich oder später erst realisiert worden.
    Ich fand 'Design und Entwicklung von C++' dazu ganz interessant, darin beschreibt Bjarne Stroustrup, wie es eigentlich zu C++ kam, was in C++ nicht reinkam, was er gerne anders gemacht hätte oder weggelassen hat.

    Ich empfinde C++ als logischer als viele andere Sprachen, weshalb ich C++ den Vorzug gebe, wo es möglich ist. In der Regel ist die Sprache eindeutig und wenigstens frei von vielen impliziten Geschichten, die mich bei Delphi (wo ich ursprünglich herkomme) genervt haben. Bisher hatte ich wenigstens wenig Probleme, die Syntax von C++ zu verstehen. Deswegen kommt mir das Verhalten vom G++ im Augenblick auch etwas spanisch vor, aber ich hoffe ja immer noch auf jemanden, der einen passenden Paragraphen aus dem Standard nennen kann, der das Verhalten erklärt.

    DefaultSeed kann ganz einfach 0 sein, und wenn 0 übergeben wurde, nimmt man die Uhrzeit. Es geht vorrangig darum, zu erkennen, ob der Standard-Konstruktor gerufen wurde oder nicht.

    Was wenn 0 wirklich ein gewünschter Seed ist? Oder MAXINT oder MININT oder jeder beliebige andere Wert? 😉
    Kommt halt auf die Anwendung an.



  • tommie-lie schrieb:

    Ich empfinde C++ als logischer als viele andere Sprachen, weshalb ich C++ den Vorzug gebe, wo es möglich ist. In der Regel ist die Sprache eindeutig und wenigstens frei von vielen impliziten Geschichten, die mich bei Delphi (wo ich ursprünglich herkomme) genervt haben. Bisher hatte ich wenigstens wenig Probleme, die Syntax von C++ zu verstehen. Deswegen kommt mir das Verhalten vom G++ im Augenblick auch etwas spanisch vor, aber ich hoffe ja immer noch auf jemanden, der einen passenden Paragraphen aus dem Standard nennen kann, der das Verhalten erklärt.

    Hey, ich sagte nicht, dass C++ schlecht wäre. Ich halte C++ für das Beste, was derzeit zu haben ist, aber das Beste was zu haben ist, muss mir ja nicht reichen, um zufrieden zu sein.

    tommie-lie schrieb:

    DefaultSeed kann ganz einfach 0 sein, und wenn 0 übergeben wurde, nimmt man die Uhrzeit. Es geht vorrangig darum, zu erkennen, ob der Standard-Konstruktor gerufen wurde oder nicht.

    Was wenn 0 wirklich ein gewünschter Seed ist? Oder MAXINT oder MININT oder jeder beliebige andere Wert? 😉
    Kommt halt auf die Anwendung an.

    *grummel* 😉

    Dann mach eine Klasse, die man implizit mit int konstruieren kann, aber auch einen (Default-)Wert repräsentieren kann, der nicht in int ausgedrückt werden kann. ;->

    ...alternativ eine Init-Funktion und beliebig viele passende Konstruktoren, aber das hatten wir ja schon 😉


Anmelden zum Antworten