Forum: PC-Programmierung Frage zu C++ Code


von commanderKeen (Gast)


Lesenswert?

Hallo zusammen, ich habe hier folgenden C++ Code und der tut nicht was 
ich erwarte, vielleicht könnt ihr mir da weiterhelfen:
1
#include "../../std_lib_facilities.h"
2
3
enum class Month { jan = 1, feb, mar, apr, mai, jun, jul, aug, sep, okt, nov, dec };
4
5
class Date {
6
public:
7
  Date(int y, Month m, int d) : y{ yy }, m{ mm }, d{ dd };
8
  
9
  // nonmodifying operations
10
  int day() const { return d; }
11
  Month month() const { return m; }
12
  int year() const { return y; }
13
    
14
private:
15
  int y;
16
  Month m;
17
  int d;    // day
18
};
19
20
ostream& operator<<(ostream& os, const Date& dd)
21
{
22
  return os << '(' << dd.year() << ',' << dd.month() << ',' << dd.day() << ')';
23
}
24
25
int main()
26
{
27
  Date today{ 1978, Month::jun, 25 };
28
  cout << "Today: " << today << endl;
29
  keep_window_open();
30
}


Ich möchte in der main-Funktion mit "cout" das Objekt "today" auf den 
Bildschirm ausgeben, aber ich erhalte folgende Fehlermeldungen:

>> Fehler E0349: Kein "<<"-Operator stimmt mit diesen Operanden überein.

>> Fehler C2679: Binärer Operator "<<": Es konnte kein Operator gefunden werden, 
der einen rechtsseitigen Operanden vom Typ "Month" akzeptiert (oder keine 
geeignete Konvertierung möglich)

Beide Fehler beziehen sich auf die Funktion mit der Operator Überladung 
(ostream& operator<<(ostream& os, const Date& dd).

Was muss ich ändern, damit das Programm in main fehlerfrei ausgeführt 
werden kann?

von Carl D. (jcw2)


Lesenswert?

commanderKeen schrieb:
>
> Was muss ich ändern, damit das Programm in main fehlerfrei ausgeführt
> werden kann?

einen operator << für das Enum Month anlegen, der dann z.B. den Monat in 
Textform in den Stream schiebt.

Du hast es ja für Date auch hinbekommen ;-)

Oder Date gibt den Monat auch als String aus und operator << für Date 
benutzt diese Methode statt month().

: Bearbeitet durch User
von commanderKeen (Gast)


Lesenswert?

Carl D. schrieb:
> Oder Date gibt den Monat auch als String aus und operator << für Date
> benutzt diese Methode statt month().

Ich stehe gerade auf dem Schlauch, wie müsste ich den code abändern?

von Carl D. (jcw2)


Lesenswert?

Z.B. so:
1
class Date {
2
public:
3
  Date(int y, Month m, int d) : y{ yy }, m{ mm }, d{ dd };
4
  
5
  // nonmodifying operations
6
  int day() const { return d; }
7
  Month month() const { return m; }
8
  int year() const { return y; }
9
10
  const char * month_str() {
11
     static const char* months[] = { "Jan", "Feb", "Mar",
12
                                     "Apr", "May", "Jun",
13
                                     "Jul", "Aug", "Sep",
14
                                     "Oct", "Nov", "Dec"
15
                                   };
16
    return months[m-1];
17
  }
18
19
private:
20
  int y;
21
  Month m;
22
  int d;    // day
23
};
24
25
ostream& operator<<(ostream& os, const Date& dd)
26
{
27
  return os << '(' << dd.year() << ',' << dd.month_str() << ',' << dd.day() << ')';
28
}

(Keine Gewähr für Fehlerfreiheit, da nur runtergetippt)

von Egon N. (egon2321)


Lesenswert?

Mir persönlich fehlen ja namespaces und die iostream. Die sind schon 
drin? Bevor ich mir da den Code weiter anschau...

von Rolf M. (rmagnus)


Lesenswert?

commanderKeen schrieb:
>>> Fehler C2679: Binärer Operator "<<": Es konnte kein Operator gefunden werden,
> der einen rechtsseitigen Operanden vom Typ "Month" akzeptiert (oder
> keine geeignete Konvertierung möglich)

Weil sich enum class nicht implizit nach int konvertieren lässt. Das ist 
einer der Unterschiede zum klassischen enum.

commanderKeen schrieb:
> Ich möchte in der main-Funktion mit "cout" das Objekt "today" auf den
> Bildschirm ausgeben, aber ich erhalte folgende Fehlermeldungen:

Wie soll es denn ausgegeben werden? Der Monat einfach als Zahl oder als 
Name?

Egon N. schrieb:
> Mir persönlich fehlen ja namespaces und die iostream. Die sind schon
> drin? Bevor ich mir da den Code weiter anschau...

Möglicherweise in dem ominösen:

commanderKeen schrieb:
> #include "../../std_lib_facilities.h"

von Dr. Sommer (Gast)


Lesenswert?

commanderKeen schrieb:
> Date(int y, Month m, int d) : y{ yy }, m{ mm }, d{ dd };

Wie funktioniert das überhaupt? Wo sind yy, mm und dd definiert?

von commanderKeen (Gast)


Lesenswert?

Danke für eure Vorschläge, mir ist auch ne Idee gekommen. Wenn ich das 
so mache, bekomme ich keine Fehlermeldung und alles läuft:
1
ostream& operator<<(ostream& os, const Date& dd)
2
{
3
  return os << '(' << dd.year() << ',' << int(dd.month()) << ',' << dd.day() << ')';
4
}

vorher war es so:
1
 ... << dd.month() << ..
jetzt so:
1
 ... << int(dd.month()) << ..

Und das "dd.month" die richtigen Werte bekommt stellt ja mein 
Konstruktor sicher. Ist das eine gute Lösung? Einfach ist sie ja, was 
nicht immer gut sein muss.

von Kassowarth von Sondermuehlen (Gast)


Lesenswert?

commanderKeen schrieb:
> : y{ yy }, m{ mm }, d{ dd }

Was ist das für eine seltsame Initialisierung?
Was sagt der Compiler dazu eigentlich?

von Dr. Sommer (Gast)


Lesenswert?

commanderKeen schrieb:
> Ist das eine gute Lösung?

Nein, C-Casts und dann auch noch in der "Funktion-Call" Syntax sind 
unschön. Sicherer und besserer ist ein named cast:
1
return os << '(' << dd.year() << ',' << static_cast<int>(dd.month()) << ',' << dd.day() << ')';
Wenn du es als Text haben willst, dann so wie Carl es schon gezeigt hat.

von commanderKeen (Gast)


Lesenswert?

Kassowarth von Sondermuehlen schrieb:
> commanderKeen schrieb:
>> : y{ yy }, m{ mm }, d{ dd }
>
> Was ist das für eine seltsame Initialisierung?
> Was sagt der Compiler dazu eigentlich?

Das ist eine Memberinitialisiererliste, in den geschweiften Klammern 
könnte auch Hunz und Kunz drin stehen, das sind die Werte mit denen man 
ein Objekt initialisiert z.B. Date {1999, Month::mar, 12}; 1999 = yy, 
Month::mar = mm usw.

Dr. Sommer schrieb:
> static_cast<int>(dd.month())

Stimmt eine explizite Umwandlung ist besser, danke.


Dr. Sommer schrieb:
> Wenn du es als Text haben willst, dann so wie Carl es schon gezeigt hat.

Ja das habe ich zur Kenntnis genommen, wollte aber nur Zahlenwerte, 
hätte ich dazu schrieben sollen. Danke dir für den Vorschlag Carl.

von Nase (Gast)


Lesenswert?

commanderKeen schrieb:
> as ist eine Memberinitialisiererliste

Ich glaube, die Frage bezog sich auf die geschweiften Klammern.

Ja, das ist neu zu C++11 dazugekommen, um an anderer Stelle die 
Mehrdeutigkeiten aufzulösen, die durch runde Klammern entstehen, und es 
nennt sich uniform initializer o.ä..

von Dr. Sommer (Gast)


Lesenswert?

Nase schrieb:
> Ja, das ist neu zu C++11 dazugekommen,

Das klärt aber immer noch nicht, wo die Ausdrücke dd, mm, yy definiert 
sind...

von Rolf M. (rmagnus)


Lesenswert?

Dr. Sommer schrieb:
> Nase schrieb:
>> Ja, das ist neu zu C++11 dazugekommen,
>
> Das klärt aber immer noch nicht, wo die Ausdrücke dd, mm, yy definiert
> sind...

Oder wo der Body des Konstruktors ist. Soweit ich weiß, darf auch in 
neueren Versionen von C++ der Konstruktor nicht ausschließlich aus der 
Initialisierungsliste bestehen.

: Bearbeitet durch User
von guest (Gast)


Lesenswert?

Dr. Sommer schrieb:
> Nein, C-Casts und dann auch noch in der "Funktion-Call" Syntax sind unschön. 
Sicherer und besserer ist ein named cast:
1
return os << '(' << dd.year() << ',' << static_cast<int>(dd.month()) << ',' << dd.day() << ')';

Nein, named casts auch explizite Typumwandlung genannt, ist nur dann 
erforderlich wenn man mittels Fehlerbehandlung eine verlustbehaftete 
Umwandlung abfangen möchte.

von Dr. Sommer (Gast)


Lesenswert?

guest schrieb:
> Nein, named casts auch explizite Typumwandlung genannt, ist nur dann
> erforderlich

Ich habe nicht gesagt dass sie erforderlich sind. Sie vermeiden aber 
Fehler, weil sie dem Compiler strengere Prüfungen ermöglichen. 
Tatsächlich können klassische C Casts alles was named casts können, und 
sogar ein bisschen mehr - diese Fälle sind aber nur aus Abwärts 
Kompatibilitäts Gründen erlaubt und sind grundsätzlich zu vermeiden.

von guest (Gast)


Lesenswert?

Man sollte beides nutzen und wissen wann man es braucht:

Implizite Typumwandlung wenn man bewusst einen Wertverlust in kauf 
nehmen kann/will

Explizite Typumwandlung wenn man einen Werteverlust vermeiden will und 
darüber mittels Fehlerbehandlung informiert werden will.

von mh (Gast)


Lesenswert?

guest schrieb:
> Implizite Typumwandlung wenn man bewusst einen Wertverlust in kauf
> nehmen kann/will
>
> Explizite Typumwandlung wenn man einen Werteverlust vermeiden will und
> darüber mittels Fehlerbehandlung informiert werden will.

c-style und "named" casts sind beide explizit. In beiden Fällen muss man 
explizit angeben, dass und wohin gecastet wird. In beiden Fällen kann es 
zu Werteverlust kommen. Und was meinst du mit Fehlerbehandlung? Fehler 
beim Compilieren, Exceptions oder was?

von guest (Gast)


Lesenswert?

mh schrieb:
> Exceptions

genau das.

von Dr. Sommer (Gast)


Lesenswert?

guest schrieb:
> genau das.

Welcher Cast gibt denn wann eine Exception?

von guest (Gast)


Lesenswert?

mh schrieb:
> In beiden Fällen kann es
> zu Werteverlust kommen

Du kannst das "static_cast..." Konstrukt in Verbindung mit Exception 
handling nutzen um eine Meldung zu erhalten wenn ein Verlust beim 
Konvertieren auftritt. Das geht mit C-Style casts nicht.

von Dr. Sommer (Gast)


Lesenswert?

Der einzige Fall wo casten Exceptions wirft ist dynamic_cast mit 
Referenzen... Aber dynamic_cast sollte man eh vermeiden und ist 95% der 
Fälle ein Zeichen von schlechter Programmstruktur.

https://ideone.com/96Xfso

von mh (Gast)


Lesenswert?

guest schrieb:
> mh schrieb:
>> In beiden Fällen kann es
>> zu Werteverlust kommen
>
> Du kannst das "static_cast..." Konstrukt in Verbindung mit Exception
> handling nutzen um eine Meldung zu erhalten wenn ein Verlust beim
> Konvertieren auftritt. Das geht mit C-Style casts nicht.

Und wie geht das? static_cast selbst wirft keine Exception. Ok, wenn die 
selbst implementierte Konvertierungsfunktion oder der Constructor eine 
Exception wird ... aber das würde auch beim c-style Cast passieren.

von Dirk K. (merciless)


Lesenswert?

Wenn man schon casten muss, dann bitte nur mit named casts.
Die kann man nämlich auch gut suchen im Sourcecode, was
bei C-casts schwierig ist.

Just my 2 cents
merciless

von Rolf M. (rmagnus)


Lesenswert?

guest schrieb:
> Man sollte beides nutzen und wissen wann man es braucht:
>
> Implizite Typumwandlung wenn man bewusst einen Wertverlust in kauf
> nehmen kann/will
>
> Explizite Typumwandlung wenn man einen Werteverlust vermeiden will und
> darüber mittels Fehlerbehandlung informiert werden will.

Ich würde sagen, genau umgekehrt. Compiler warnen, wenn durch eine 
implizite Konvertierung ein Wertverlust eintreten könnte.
Mit einer expliziten Typumwandlungen sage ich dem Compiler dagegen, dass 
ich das genau so will, auch wenn es einen Wertverlust bedeuten könnte -> 
keine Compiler-Warnung mehr.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.