Frage zum iterator (error C2679)
-
hi all,
ich bin dabei ein Programm zu schreiben das mir binär die Daten in eine Datei schreiben soll. Momentan häng ich aber leider an dem Fehler error C2679: Binärer Operator '=': Es konnte kein Operator gefunden werden, der einen rechtsseitigen Operanden vom Typ 'std::list<_Ty>::_Const_iterator<_Secure_validation>' akzeptiert (oder keine geeignete Konvertierung möglich)
Hier mal mein Code. Würd mich freuen wenn mir einer weiterhelfen könnte oder mir Tipps geben kann falls einem was auffällt was ich verbessern könnte.
Laut compiler soll der fehler in der Datei test.cpp zeile 21 liegen. Ich denk mal es liegt am iterator, momentan sehe ich aber da keinen fehler xD
test.h
#pragma once #include <list> #include <iostream> #include <fstream> #include <string> class test{ private: int nummer; std::list<double> l; double stand; public: test(int = 0); ~test(void); std::ostream& write(std::ostream &) const; std::istream& read(std::istream &); };test.cpp
#include <iterator> #include <list> #include "test.h" using namespace std; test::test(int n){ nummer = n; stand = n*3-1; for(int i = 0; i < n; i++) l.push_back(i); } test::~test(void){} ostream& test::write(ostream &f) const{ f.write((char *) &nummer, sizeof(int)); int groesse = l.size(); // numbers of elemts in list f.write((char *) &groesse, sizeof(int)); list<double>::iterator it; for(it = l.begin(); it != l.end(); it++) // write the elements f.write((char *) &(*it), sizeof(double)); f.write((char *) &stand, sizeof(double)); return(f); } istream& test::read(istream &f){ f.read((char *) &nummer, sizeof(int)); int groesse = 0; f.read((char *) &groesse, sizeof(int)); // for to read the elements and push_back in list f.read((char *) &stand, sizeof(double)); return(f); }
-
da deine memberfunktion const ist, hast du auch nur zugriff auf ein konstantes list<double> objekt, und die funktion begin und end liefern in diesem fall nicht
std::list<double>::iteratorsondernstd::list<double>::const_iterator.im übrigen erwartet write einen zeiger auf const char, caste also lieber nach const char*, und verwende dafür am besten
reinterpret_cast<const char*>(&*it)
-
noch ein paar Kleinigkeiten die mir beim Lesen deines Codes aufgefallen sind:
- die header <fstream> und <string> in deinem header sind überflüssig, da du nirgendwo einen std::string oder einen std::fstream benutzt
- der header <li>t in der cpp ist überflüssig, da du ihn durch den header auf jeden fall mit einbindest.
- an Stelle des headers <iostream> reicht es, in deinem header <iosfwd> zu includen (der liefert forward deklarationen für so ziemlich alles im <iostream>), das spart compilierzeit. dafür dann in der cpp ein #include <iostream>
- bevorzuge präfix-inkrement in for-schleifen. wenns nicht grade int & Co ist, wird für das postfix-inkrement eine kopie angelegt, das objekt inkrementiert und dann die Kopie zurückgegeben. präfix inkrementiert und gibt dann eine referezn auf das objekt zurück ohne Kopie. da du beim schleifen hochzählen den Rückgabewert nicht brauchst erzeugt dir das postfix-inkrement jedesmal eine kopie, die du nicht benutzt - sinnlose Zeitverschwendung
- deine funktionen benutzen häufig unformatierten input bzw output, genauer gesagt schreibst du die binäre repräsentation der variablen auf. Mal abgesehen davon, dass das nicht gerade portabel ist (die interne repräsentation kann von sytsem zu system udn von compiler zu compiler variieren), kannst du dir den Aufwand etwas verringern indem du dir für den immer wiederkehrenden Teil eine eigene funktion schreibst, die du dann aufrufst:template <typename T> void bin_out(ostream& os, T const& t) { os.write(reinterpret_cast<const char*>(&t), sizeof(T)); return os; } template <typename T> void bin_in(istream& is, T& t { os.read(reinterpret_cast<char*>(&t), sizeof(T)); return is; }am Ende sieht das dann so aus:
test.h#pragma once #include <list> #include <iosfwd> class test{ private: int nummer; std::list<double> l; double stand; public: test(int = 0); ~test(void); std::ostream& write(std::ostream &) const; std::istream& read(std::istream &); };test.cpp
#include <iostream> #include "test.h" using namespace std; template <typename T> void bin_out(ostream& os, T const& t) { os.write(reinterpret_cast<const char*>(&t), sizeof(T)); return os; } template <typename T> void bin_in(istream& is, T& t { os.read(reinterpret_cast<char*>(&t), sizeof(T)); return is; } test::test(int n){ nummer = n; stand = n*3-1; for(int i = 0; i < n; i++) l.push_back(i); } test::~test(void){} ostream& test::write(ostream &f) const{ bin_out(f, nummer); int groesse = l.size(); // numbers of elemts in list bin_out(f, groesse); list<double>::iterator it; for(it = l.begin(); it != l.end(); it++) // write the elements bin_out(f, (*it)); bin_out(f, stand); return(f); } istream& test::read(istream &f){ bin_in(f, nummer); int groesse = 0; bin_in(f, groesse); // for to read the elements and push_back in list bin_in(f, stand); return(f); }Viel übersichtlicher, oder?
Und wenn du wirklich fancy werden willst, schreibst du dir parametrisierte Manipulatoren:
template <class T> struct BinOut { T const& t_; BinOut(T const& t) : t_(t) {}; } template<class T> std::ostream& operator<<(std::ostream& os, BinOut<T> const& bo) { os.write(reinterpret_cast<const char*>(&(bo.t_)), sizeof(T)); return os; } template <class T> BinOut<T> bin_out(T const& t) { return BinOut<T>(t); } ostream& test::write(ostream &f) const{ int groesse = l.size(); // numbers of elemts in list f << bin_out(nummer) << bin_out(groesse); list<double>::iterator it; for(it = l.begin(); it != l.end(); it++) // write the elements f << bin_out(*it); f << bin_out(stand); return(f); }Die Funktionsweise ist simpel: bin_out liefert ein Objekt vom Typ BinOut, für das der Stream-operator überladen wurde. Man hätte auch direkt schreiben können
f << BinOut<int>(stand);wo man aber erst den typ als template-parameter dazuschreiben muss - die Funktion erspart uns das.
-
Mal nen paar andere Sachen, die viele oftmals vergessen. Auch du pumuckl hast es nicht drin

Also std::size_t muss nicht zwingend auf einer Platform als unsigend int definiert sein. Aber es ist auf jedenfall ein positiver Ganzzahl-Datentyp. D.h. ist int auf jedenfall nicht richtig um std::size_t zu ersetzen

Dann könnte man auch mal sowas nettes wie eine Initialisierungsliste nutzen. Hat was!
Warum schreibst du eine extra Funktion read/write, und überlädst nicht einfach den dafür vorgesehenen Streamoperator?

Einen Konstruktor/Destruktor/Zuweisungsoperator, der nicht extra implementiert werden muss(weil da mist rauskäme, wenn man es den Kompiler machen ließe), kannst du einfach weglassen. Das macht der Compiler!
Achja, nicht alle Compiler unterstützen #pragma once! Guck dir alternative Include-Guards an!
#if !defined(TEST_HPP__INCLUDED) #define TEST_HPP__INCLUDED #if (_MSC_VER >= 1300) #pragma once #endif // (_MSC_VER >= 1300) #include <list> #include <iostream> class test { unsigned int m_number; std::list<double> m_data; double m_state; public: test(const unsigned int number = 0) : m_number(number), m_state(number * 3.0 - 1.0) { for (unsigned int i(0); i < number; ++i) m_data.push_back(i); } friend std::ostream& operator<<(std::ostream& out, test const& data) { out.write(reinterpret_cast<const char*>(&data.m_number), sizeof(unsigned int)); const std::size_t size_list(data.m_data.size()); out.write(reinterpret_cast<const char*>(&size_list), sizeof(std::size_t)); for (std::list<double>::const_iterator it(data.m_data.begin(); it != data.m_data.end(); ++it) out.write(reinterpret_cast<const char*>(&(*it)), sizeof(double)); return (out.write(reinterpret_cast<const char*>(&data.m_state), sizeof(double))); } friend std::istream& operator>>(std::istream& in, test& data) { /* ... mach mal selbst ;) */ } }; #endif // TEST_HPP__INCLUDED... hmm also deine Schleife kann man noch durch std::for_each und dann schön Functor (s. bin_out von pumuckl) schreiben ...
std::for_each(data.m_data.begin(), data.m_data.end(), std::bind1st(bin_out<double>, out));... aber das ist ein wenig sehr fortgeschritten

-
(D)Evil schrieb:
Warum schreibst du eine extra Funktion read/write, und überlädst nicht einfach den dafür vorgesehenen Streamoperator?

Da Streamoperatoren oftmals auf interne (=private) Daten der Klasse zugreifen müssen kann man entweder die Operatoren als friends deklarieren oder aber read/write Methoden zur Verfügung stellen und die operatoren zu inline-einzeilern machen, ganz ohne friends (friends sind mir immer bissl suspekt weil sie ein wenig an der kapselung rumbröseln ;))
Einen Konstruktor/Destruktor/Zuweisungsoperator, der nicht extra implementiert werden muss(weil da mist rauskäme, wenn man es den Kompiler machen ließe), kannst du einfach weglassen. Das macht der Compiler!
Moment. Wenn mans den Compiler machen lässt, kommt mist raus, also lässt mans weg, damits der Compiler macht? Hö??
Also da die list, der int und der double problemlos standar-kopiert werden können, wären die compilergenerierten op= und Copy-Ctor doch nicht schlecht.
-
Jo meinte ich auch und wollte den Threadersteller darauf hinweißen, das er keinen eigenen Destruktor definieren muss

-
danke euch für die Hilfe, funktioniert jetzt alles.

gruß
old.school
-
pumuckl schrieb:
(friends sind mir immer bissl suspekt weil sie ein wenig an der kapselung rumbröseln ;))
-
Ok, gelesen und akzeptiert

Trotzdem spare ich mir friends, wenn das ohne nachteile möglich ist. Und ein inline-operator<<-Einzeiler bringt nunmal keine Nachteile