Schwierigkeiten mit templates



  • Hallo zusammen,

    leider finde ich im Netz nichts, auch hier über die Suche nicht - oder ich hab die falschen Suchbegriffe... 😃

    Gerade bin ich beim Thema templates und ich habe einen Code geschrieben in dem ich das ausprobieren wollte.
    Wie ihr nachfolgend seht, möchte ich ein Array per Eingaben füllen und sortieren lassen.

    Bitte helft mir bei folgenden Problemen:

    1. In der template-Funktion kommen die Arrays nicht an, bzw. nach der Übergabe der Arrays hat das int Array nur 1 Element, anstatt 5. Das string Array hat 0 Elemente.
    -> Warum..? 😕

    2. Wie kann ich die Eingabe der Werte realisieren..?
    getline( cin, Array[i] ) funktioniert ja nicht, weil sie ja nur strings akzeptiert und beim Aufruf mit dem int Array ist das natürlich Mist.
    Und bei cin >> Array[i] hätte ich ja das Problem dass ich, bei Texteingabe, die Eingabe nur bis zum ersten Leerzeichen erhalte.
    cin.ignore( 1, '\n' ) hilft auch nicht, weil ich beim int Array ja dann zwei Eingaben machen MUSS und der string ist ja so trotzdem begrenzt.
    -> oder ist das gar nicht möglich beide Varianten auf einmal abzudecken..? 😮

    3. Gibt es eine elegantere Methode die Anzahl der Elemente eines Arrays zu ermitteln wie meine..?

    Hier mal der Code:

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    /*	Prototypen
    	-------------------------------------------------------------------------- */
    /* --> Template Anzahl der Array Elemente ermitteln */
    template <typename type>
    inline int intAnzahlArrayElemente( type Array[] )
    {
    	/* Rückgabe der Anzahl */
    	return( sizeof( Array ) / sizeof( type ) );
    }
    
    /* --> Template Elemnte des Arrays eingeben */
    template <typename type>
    inline void voidElementeEingeben( type Array[] )
    {
    	int intElemente;
    	type typeEingabe;
    	intElemente = intAnzahlArrayElemente( Array );
    
    	for ( int i = 0; i < intElemente; i++ )
    	{
    		cout << "  " << i+1 << ". Wert: ";
    
    		/* ----------------------------------------------------------
    			Hier möchte ich vom User Eingaben abfragen, damit
    			das aktuelle Array damit gefüllt wird
    
    			--> wie kann ich das lösen..??
    
    		   ---------------------------------------------------------- */
    	}
    }
    
    /* --> Template Array sortieren */
    template <typename type>
    inline void voidSortiere( type Array[] )
    {
    	int intElemente;
    	type type_tmp;
    
    	intElemente = intAnzahlArrayElemente( Array );
    
    	for ( int i = 0; i < ( intElemente-1 ); i++ )
    	{
    		for ( int j = i; j < ( intElemente ); j++ )
    		{
    			if ( Array[ i ] > Array[ j ] )
    			{
    				type_tmp   = Array[ i ];
    				Array[ i ] = Array[ j ];
    				Array[ j ] = type_tmp;
    			}
    		}
    	}
    
    	for ( int i = 0; i < intElemente; i++ )
    	{
    		cout << "     " << Array[ i ] << '\n';
    	}
    }
    
    /* --> Template Klasse */
    template < typename type1, typename type2, typename type3 >
    class tmpKlasse
    {
    	private:
    		string strObjektName;
    		type1 Var1;
    		type2 Var2;
    		type3 Var3;
    
    	public:
    		tmpKlasse(string strOName, type1 Var1_, type2 Var2_, type3 Var3_):
    			strObjektName(strOName), Var1(Var1_), Var2(Var2_), Var3(Var3_)
    			{ cout << "Objekt " << strObjektName << " -> erzeugt..!\n"; };
    
    		type1 type1GetVar1(){ return Var1; }
    		type2 type2GetVar2(){ return Var2; }
    		type2 type3GetVar3(){ return Var3; }
    
    		void voidDatenausgabe()
    		{
    			cout << "  Var1: " << Var1 << '\n'
    				 << "  Var2: " << Var2 << '\n'
    				 << "  Var3: " << Var3 << "\n\n";
    		}
    };
    
    /*	Hauptprogramm
    	-------------------------------------------------------------------------- */
    void main()
    {
    	int intArray[5];
    	string strArray[7];
    
    	cout << "Template Funktionen\n"
    		 << "-----------------------------------------\n\n"
    		 << "  intArray befuellen\n"
    		 << "  ------------------\n";
    	voidElementeEingeben<int>( intArray );
    
    	cout << "  strArray befuellen\n"
    		 << "  ------------------\n";
    	voidElementeEingeben<string>( strArray );
    
    	cout << "\n  Sortierte Ausgabe der Arrays verschiedener Typen\n"
    		 << "  ------------------------------------------------\n"
    		 << "  -> int-Array:\n";
    	voidSortiere<int>( intArray );
    
    	cout << "\n  -> string-Array:\n";
    	voidSortiere<string>( strArray );
    
    	cout << "\n-----------------------------------------------\n"
    		 << "-----------------------------------------------\n\n"
    		 << "Template Klasse\n\n"
    		 << "-----------------------------------------\n\n";
    	/* Objekte aus der tmpKlasse erzeugen */
    	tmpKlasse< string, string, string> tmpK_Objekt1( "1 - nur string-Attribute", "Klasse", "mit", "String Attributen" );
    	      /* < Argumentliste..!!     > */
    	tmpKlasse< string, int, float> tmpK_Objekt2( "2 - gemischte Attribut-Typen", "Klasse mit int & float", 50, 326.14 );
    
    	cout << "\n  Ausgabe uber Klassen-Mehtode:\n"
    		 << "-------------------------------\n";
    	tmpK_Objekt1.voidDatenausgabe();
    	tmpK_Objekt2.voidDatenausgabe();
    
    	cout << "\n  Ueber getter-Methode:\n"
    		 << "-------------------------------\n"
    		 << "  Var1: " << tmpK_Objekt1.type1GetVar1() << '\n'
    		 << "  Var2: " << tmpK_Objekt1.type2GetVar2() << '\n'
    		 << "  Var3: " << tmpK_Objekt1.type3GetVar3() << "\n\n"
    		 << "  Var1: " << tmpK_Objekt2.type1GetVar1() << '\n'
    		 << "  Var2: " << tmpK_Objekt2.type2GetVar2() << '\n'
    		 << "  Var3: " << tmpK_Objekt2.type3GetVar3() << "\n\n";
    
    	system("Pause");
    }
    

    PS: die Klasse funktioniert einwandfrei - trotzdem bin ich natürlich für Verbesserungsvorschläge offen..! 😋

    Danke schon mal im Voraus..!! 😃

    Gruß

    Eisenspitze



  • Arrays lassen sich nicht als Parameter übergeben.

    void foo(T a[])
    

    ist äquivalent zu

    void foo(T* a)
    

    Nimm entweder eine Referenz auf ein Array, oder noch viel besser: std::array.



  • oder std::vector...

    zumal das hier auch nicht geht:

    template <typename type>
    inline int intAnzahlArrayElemente( type Array[] )
    {
        /* Rückgabe der Anzahl */
        return( sizeof( Array ) / sizeof( type ) );
    }
    

    da der parameter ja "type * array" ist wird hier nur die grösse eines typ eines zeigers(der auf einem system normalerweise immer konstant ist) durch die grösse des typs selbst geteilt

    bsp:
    int auf 32Bit Betriebssystem -> 4 / 4 = 1
    double auf 32Bit Betriebssystem -> 8 / 4 = 2

    aber das ist unabhängig von deinem array...

    um trotzdem die grösse zu behalten musst du sie entweder mitspeichern oder durch z.b. Nullterminierungen berechenbar machen
    es gibt auch einen hack mit templates, aber da müssten die profis was zu sagen



  • Wow, das ging ja fix..ich bin echt beeindruckt... 😃 😃

    Leider hab ich Schwierigkeiten das zu verstehen. 😕
    Mir ist klar dass

    void foo( T a[] )
    

    gleich

    void foo( T *a )
    

    ist - aber warum std::array..?
    Ist das durch

    using namespace std;
    

    nicht sowieso schon erledigt..?
    Zudem - bei std::array oder std::vektor meckert Visual... 😕

    Den Zugriff per Referenz werde ich jetzt mal ausprobieren. 😉

    Das mit der Ermittlung der Anzahl leuchtet mir ein, d.h. das letzte Element im Array müsste \0 sein..?



  • jain, das kann man so pauschal nicht sagen, da gibt es viele andere probleme.
    \0 als terminierung zu verwenden macht nur selten sinn, z.b. bei strings da die 0 als char selten vorkommt und auch nicht üblich ist als normales zeichen.
    aber in einem int array kommt es schon öfters mal vor dass es nullen gibt, und das nicht am ende 🙂
    bei gleitkommazahlen kann es, wenn es blöd kommt, auch passieren, dass sich die 0 nicht richtig darstellen lässt und damit nicht ganz null ist.

    das einzige was in dem fall wirklich hilft, ist sich die grösse zu merken. und daher, wieso das rad neu erfinden wenn es schon zig mal neu erfunden wurde?!
    ==>
    zu vector
    zu array(ein eingekapseltes statisches array, was mehr sicherheit und funktionalität bietet als das rohe array)



  • ...in einem int array kommt es schon öfters mal vor dass es nullen gibt, und das nicht am ende

    stimmt, da war ja noch was... :p

    Danke für die Links..! Das werd ich mir mal anschauen.

    Hast du vielleicht auch noch einen Tipp für die Abfrage der Daten..? (Problem Nr. 2) 😕



  • tja generisch ist das alles nicht so gut.

    halbwegs gut klappts vielleicht wenn du eine string zeile einliest, diese dann in einen stringstream stopfst und daraus dann dein datum rausholst

    guckst du hier:

    std::string s;
    std::getline(std::cin, s);
    
    std::stringstream ss;
    ss << s;
    
    type x;
    ss >> x;
    

    aber da musst du dir über die fehlerbehandlung im klaren sein.



  • Okay, dass ist mir bekannt..zumindest jetzt wieder in Erinnerung... 😃

    Mit Fehlerbehandlung meinst du

    cin.fail()
    

    oder..?

    Wie kommst du auf Datum..?

    Mal so nebenbei, das Bsp. habe ich mir frei einfach so ausgedacht - war das einfach Mist..? Also ich meine wie oft würde man sowas als template anlegen, eine Funktion die Text & Zahlen verarbeiten soll..? ..deiner Erfahrung nach..?



  • weniger die fail bits von den streams sondern eher, was soll passieren wenn man zahlen und buchstaben mischt und eig eine zahl eingeben will. solche fälle halt, die müssen wohl überlegt sein

    tja, mit erfahrung mangelts bei mir. dazu kann ich nur sagen, ich hab keien erfahrung...

    aber ich wäre da halt ziemlich vorsichtig was die eingabe betrifft, da kann man den user mit richtig nerven, man kann die sicherheit des programms und damit des computers anfechten (guck mal den tanenbaum zum thema betriebssystem sicherheit, buffer overflow usw.)

    edit: eingaben sollte man weniger generisch machen, da kann man ruhig konkreter werden



  • Skym0sh0 schrieb:

    fail bits

    Es gibt keine fail bits. Nur error-bits und das failbit.

    Wieso machst du überhaupt die Pufferung durch den Stringstream? Gerade std::getline mit einem LF als Delimitter ist nicht gut, da ein überladener left-shift Operator auch

    std::ostream& operator<<(std::ostream& os, T const& t)
    {
        return os << t.getThis() << '\n' << t.getThat();
    }
    

    so aussehen kann (unsinnig aber möglich).

    aber ich wäre da halt ziemlich vorsichtig was die eingabe betrifft, da kann man den user mit richtig nerven, man kann die sicherheit des programms und damit des computers anfechten (guck mal den tanenbaum zum thema betriebssystem sicherheit, buffer overflow usw.)

    😕
    Wenn mann es richtig macht dann ist daran garnix nervig oder gefährlich...



  • Also std::array ist eine tolle Sache (ebenso std::vector), danke..! 😃 😃

    Nur mit

    std::string s;
    std::getline(std::cin, s);
    
    std::stringstream ss;
    ss << s;
    
    type x;
    ss >> x;
    

    klappt´s dann leider nicht, der Compiler gibt mir da

    error C2678: Binärer Operator '>>': Es konnte kein Operator gefunden werden, der einen linksseitigen Operanden vom Typ 'std::stringstream' akzeptiert (oder keine geeignete Konvertierung möglich)

    aus. 😕

    So wie ich die Arrays am Anfang erstellt und übergeben habe, funktioniert es so (beim testen hab ich die Array-Größe mit übergeben 😃 )

    Also danke noch mal für eure (schnelle..!!) Hilfe..! 😃 😃 😃



  • Ach so, ja daran hab ich im Übungsprogramm jetzt natürlich nicht gedacht, ist ja nur meins an dem ich mir das beibringe... 😃
    Du hast natürlich Recht, das ist wichtig und mir auch bekannt. In VBA habe ich schon einige Programme für meine damalige Firma geschrieben, da hab ich schon gemerkt dass die Benutzer (mich selber eingeschlossen) beim verwenden gerade die Sachen eingeben an die man selber (beim Programmieren) am wenigsten denkt... :p

    @Sone
    Wie würdest du das lösen..?



  • Wie würde ich eine Menge von Zahlen von der Konsole einlesen?

    std::vector<int> vec(5);
            for(size_t n(0);n < 5;++n)
    
                    while(!(std::cin >> vec[n]))
                    {
                            std::cout << "Error! Type in a number, please!\n";
                            std::cin.clear();
                            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
                    }
    


  • Wie würde ich eine Menge von Zahlen von der Konsole einlesen?

    Hm, aber es ging ja darum eine Lösung zu finden, dass ich es in der tamplate Funktion hinbekomme int UND strings einzulesen.
    Mit dem selben Code.
    Und zwar so, dass das einlesen der Eingabe bei einer Zahl nach der Zahl endet, wie es cin << Var eben tut; und das Einlesen nach der Zeilenschaltung, wie es getline() eben tut.

    Für Zahlen funktioniert deine Methode natürlich, aber beim string hilft das wenig..oder verstehe ich daran irgendetwas nicht..? 😉



  • Eisenspitze schrieb:

    Für Zahlen funktioniert deine Methode natürlich, aber beim string hilft das wenig..oder verstehe ich daran irgendetwas nicht..? 😉

    Wunderbar funktioniert das für Strings. Probier es doch aus.



  • Natürlich hab ich das probiert bevor ich dir geschrieben hab - ich bin doch auf der Suche nach einer Lösung..!

    Leider klappt es eben nicht..!

    Das ist der Code:

    #include <string>
    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    void main()
    {
    	std::vector<string> strvec(5);
            for(size_t n(0);n < 5;++n)
            {
                    while(!(std::cin >> strvec[n]))
                    {
                            std::cout << "Error! Type in a number, please!\n";
                            std::cin.clear();
                            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
                    }
            }
    
    		for(size_t n(0);n < 5;++n)
    		{cout << strvec[n] << "\n";}
    
    	system("Pause");
    }
    

    Also genau deiner nur eben für strings.

    Wenn ich:

    "Hallo Du"
    "ein Test"
    "und geht nicht"

    eingebe, kommt

    Hallo
    Du
    ein
    Test
    und

    heraus..!



  • Ahh. Natürlich. Jetzt verstehe ich dein Problem. Der operator>> liest nur bis zum ersten Whitespace...
    Vielleicht machst du eine Spezialisierung?

    Ach, btw: void main() ? Ist das dein ernst?



  • Okay, flags hab ich bisher nur ein paar mal so neben bei beim suchen gelesen, aber noch nie bewusst irgendwo wahrgenommen...

    Kannst du mir da auf die Sprünge helfen..? Hab grad im Netz gesucht und rumgespielt, aber ich bekomm´s nicht hin... 😞

    Meinst du ios::skipws..?

    Spezialisierung ist mir bekannt, nur wie muss ich´s mit ios::skipw schreiben..?

    Achso, das void main hab ich nur drin weil das im Skript so stand, wobei ich irgendwo im Hinterkopf hab dass es immer int main sein MUSS.
    -> stimmt das..?
    -> welche Hintergründe gibt´s da..?



  • Nun, mit Flags habe ich nichts gefunden, vielleicht jemand anders.

    Was du machen könntest ist eine Überladung mit einer Templatefunktion:

    template<typename t>
    std::istream& readFrom(std::istream& is, t& toread)
    {
        return is >> toread;
    }
    
    std::istream& readFrom(std::istream& is, std::string &toread)
    {
        return std::getline(is, toread);
    }
    


  • ja müsste inmain() heissen. warum? weil das der standard besagt als programmeinstiegspunkt

    du musst dir das so vorstellen, dein compiler bettet dein programm in eine laufzeitumgebung ein, die in etwa so aussieht:

    someFunction();
    // ...
    int returnValue = main();
    if ( returnValue == ... )
        ...
    somefunction();
    

    void main ist halt kein standardkonformes C++

    bei deinem letzten programm lässt du auch nur 5 worte eingeben durch die schleifen, daher nicht wundern wenn nur 5 wieder rauskommen


Anmelden zum Antworten