Hi, ich versuche mich gerade daran, in C++ Operatoren zu überladen.
Der Code sieht so aus:
1
classV
2
{
3
public:
4
signedcharv;
5
6
inlineV(){};
7
};
8
9
staticinlineVoperator+(Va,constVb)
10
{
11
a.v+=b.v;
12
returna;
13
}
14
15
Vadd(constVa,constVb)
16
{
17
returna+b;
18
}
19
20
Vset(void)
21
{
22
Vv;
23
v.v=123;
24
returnv;
25
}
26
27
unsignedintsizeV=sizeof(V);
Leider ist die Codegröße jenseits von Gut und Böse: Alle Operationen
werden über den Frame bzw. this abgewickelt.
In dem C-Projekt verwende ich 8-Bit Variablen für Fixpunkt-Arithmetik,
d.h. Q1.8, Q0.8 und 2-dimensionale Vektoren darauf. Das funktioniert
soweit auch ganz prima und effizient, allerdings wird die C-Quelle durch
die zig Arithmetik-Makros nicht gerade hübsch. Daher versuche ich mich
gerade an C++, um Operatoren überladen zu können und so eine
überschbarere Quelle zu bekommen. Zugegebenermassen hab ich immer einen
Bogen um C++ gemach -- auf dem PC hab ich immer lieber zu was richtig
objektorientiertem gegriffen wie Java.
Wie auch immer... Der Code, den GCC für das kleine Beispiel erzeugt, ist
der Overkill; sowohl die Addition als auch das Setzen der Variablen
brauchen eigentlich je nur einen Befehl. Überladen, Exceptions,
virtuelle Methoden, Vererben brauch ich nicht und will sie hier auch
garnicht -- ich will nur die kompakte Syntax der Operatoren-Überladung
für + und *.
Was mir aufgefallen ist, ist daß der Konstruktor als weak angelegt wird,
so daß es nicht geinlint werden kann vom Compiler. Wie vermeidet man
das?
Bzw. wie ist die Quelle hinzuschreiben, daß C++ sich nicht wegen
inakzeptabel breitem und langsamen Code raushaut und mit C mithalten
kann?
zur codegröße kann ich nichts sagen, aber das du es dem compieler nicht
gerade leicht machts erkenne ich.
> static inline V operator+ (V a, const V b)
damit wird bei jeder übergeben eine Kopie von a und b angelegt mit alles
was dazu gehört (Construktor/Destruktor)
viel besser sollte es so sein
class V
{
public:
signed char v;
inline V(){};
inline V( signed char value): v(value) {};
};
static inline V operator+ (const V& a, const V& b)
{
return V(a.v + b.v);
};
>zur codegröße kann ich nichts sagen, aber das du es dem compieler nicht>gerade leicht machts erkenne ich.
Vom OP:
>static inline V operator+ (V a, const V b)>{> a.v += b.v;> return a;>}
Und außerdem zeigt das Beispiel, warum operator overloading geteiltes
Echo in der Programmiergemeinde auslöst, und mich immer wieder vom
Wunsch abbringt, sowas auch in Java zu haben:
der OP verändert einen der Operanden der Addition, was natürlich niemand
erwarten würde. Sobald jemand anderes dann den Code verwendet, wird er
große Augen machen. Also doch bitte Finger weg vom Überladen von
Operatoren, wenn man die Kontrakte nicht einhalten will.
>static inline V operator+ (const V& a, const V& b)>{> return V(a.v + b.v);>};
schon besser. ;-)
Peter schrieb:> zur codegröße kann ich nichts sagen, aber das du es dem compieler nicht> gerade leicht machts erkenne ich.>>> static inline V operator+ (V a, const V b)>> damit wird bei jeder übergeben eine Kopie von a und b angelegt mit alles> was dazu gehört (Construktor/Destruktor)
sizeof(V) ist 1, also ist es nicht nötig, mehr als 1 Byte zu übergeben
(denk ich mir so al C++ Noob). Zumindest wenn alles andere statisch
bekannt ist, also kein virtueller Krempel etc.
> viel besser sollte es so sein [...]
Ok, machen wir es explizit:
1
classV
2
{
3
public:
4
5
signedcharv;
6
7
inlineV(){};
8
inlineV(signedcharvalue):v(value){};
9
};
10
11
staticinlineVoperator+(constV&a,constV&b)
12
{
13
returnV(a.v+b.v);
14
};
15
16
Vadd(constVa,constVb)
17
{
18
returna+b;
19
}
20
21
Vset(void)
22
{
23
returnV(128);
24
}
25
26
unsignedintsizeV=sizeof(V);
ARGL, mann bin ich blöd, ich hatte -Os vergessen. Peinlich. Jetzt
sieht's schon besser aus :-)
High Performer schrieb:> der OP verändert einen der Operanden der Addition, was natürlich niemand> erwarten würde. Sobald jemand anderes dann den Code verwendet, wird er> große Augen machen. Also doch bitte Finger weg vom Überladen von> Operatoren, wenn man die Kontrakte nicht einhalten will.
das ist hier aber nicht der fall, weil er voher schon eine Kopie von dem
Object gemacht hat.
High Performer schrieb:> Und außerdem zeigt das Beispiel, warum operator overloading geteiltes> Echo in der Programmiergemeinde auslöst, und mich immer wieder vom> Wunsch abbringt, sowas auch in Java zu haben:>> der OP verändert einen der Operanden der Addition, was natürlich niemand> erwarten würde.
Er verändert keinen Operanden, auch wenn das ein Java-Programmierer
nicht erwarten würde, weil der den Unterschied zwischen call-by-value
und call-by-reference nicht kennt. ;-)
>>static inline V operator+ (const V& a, const V& b)>>{>> return V(a.v + b.v);>>};>> schon besser. ;-)
Nicht besser, aber zumindest auch nicht schechter.
Johann L. schrieb:>> damit wird bei jeder übergeben eine Kopie von a und b angelegt mit>> alles was dazu gehört (Construktor/Destruktor)>> sizeof(V) ist 1, also ist es nicht nötig, mehr als 1 Byte zu übergeben> (denk ich mir so al C++ Noob). Zumindest wenn alles andere statisch> bekannt ist, also kein virtueller Krempel etc.
Da denkst du richtig. Eine Übergabe per Referenz wäre hier sogar
deutlich ineffizienter als per Kopie, wenn es sich nicht gerade um
Inline-Funktionen handeln würde, wo der Compiler die Referenzierung
einfach wegoptimieren kann. Hier sollte es also absolut keinen
Unterschied machen.
Die meisten C++-Programmierer sind es nur gewöhnt, skalare Typen per
Kopie und Klassen per Referenz zu übergeben, deshalb macht so eine
Stelle im Code immer etwas hellhörig.
Rolf Magnus schrieb:>>> damit wird bei jeder übergeben eine Kopie von a und b angelegt mit>>> alles was dazu gehört (Construktor/Destruktor)>>>> sizeof(V) ist 1, also ist es nicht nötig, mehr als 1 Byte zu übergeben>> (denk ich mir so al C++ Noob). Zumindest wenn alles andere statisch>> bekannt ist, also kein virtueller Krempel etc.>> Da denkst du richtig. Eine Übergabe per Referenz wäre hier sogar> deutlich ineffizienter als per Kopie, wenn es sich nicht gerade um> Inline-Funktionen handeln würde, wo der Compiler die Referenzierung> einfach wegoptimieren kann. Hier sollte es also absolut keinen> Unterschied machen.> Die meisten C++-Programmierer sind es nur gewöhnt, skalare Typen per> Kopie und Klassen per Referenz zu übergeben, deshalb macht so eine> Stelle im Code immer etwas hellhörig.
Anders geht das doch nicht, oder? Jedenfalls erlaubt C++ es nicht,
Opertoren für Skalare zu überladen.
BTW, was hat das Thema im Forum "PC-Programmierung" zu suchen?
Es geht doch darum, welchen Code avr-g++ erzeugt. avr-g++ läuft zwar auf
einem PC, aber das interessiert hier eher weniger. Fokus ist die
Codeerzeugung von G++ für AVR -- auch wenn's nicht explizit im Thema
steht.
Johann L. schrieb:> Codeerzeugung von G++ für AVR -- auch wenn's nicht explizit im> Thema steht.
Es steht weder im Titel noch im Text das es um die Codeerzeugung von g++
für AVR im speziellem geht. Die Frage hat auch für PC Architekturen und
andere Compiler Gültigkeit, weil dein Problem nicht der avr-g++ ist
sonder C++ im allgemeinen.
Johann L. schrieb:>> Die meisten C++-Programmierer sind es nur gewöhnt, skalare Typen per>> Kopie und Klassen per Referenz zu übergeben, deshalb macht so eine>> Stelle im Code immer etwas hellhörig.>> Anders geht das doch nicht, oder? Jedenfalls erlaubt C++ es nicht,> Opertoren für Skalare zu überladen.
Die Frage nach dem Passing Mechanismus von Funktionsargumenten stellt
sich ja nicht nur für Operatoren, sondern für Funktionen ganz allgemein.
Hier muss man in C++ ein wenig mehr mitdenken, als in Java oder C#
Johann L. schrieb:>> Die meisten C++-Programmierer sind es nur gewöhnt, skalare Typen per>> Kopie und Klassen per Referenz zu übergeben, deshalb macht so eine>> Stelle im Code immer etwas hellhörig.>> Anders geht das doch nicht, oder? Jedenfalls erlaubt C++ es nicht,> Opertoren für Skalare zu überladen.
Den Zusammenhang verstehe ich jetzt nicht. Außerdem kann man durchaus
auch Skalare als Operanden haben. Es dürfen nur nicht alle Operanden
Skalare sein.
Läubi .. schrieb:> Johann L. schrieb:>> Codeerzeugung von G++ für AVR -- auch wenn's nicht explizit im>> Thema steht.> Es steht weder im Titel noch im Text das es um die Codeerzeugung von g++> für AVR im speziellem geht. Die Frage hat auch für PC Architekturen und> andere Compiler Gültigkeit, weil dein Problem nicht der avr-g++ ist> sonder C++ im allgemeinen.
Ja stimmt, es geht nur aus dem angehängten Ausgabe von avr-g++ hervor.
Codegröße zu diskutieren ist schon was, daß man im Hinblick auf die
Zielarchitektur machen sollte. War ungeschickt von mit, das nicht
nochmal zu erwähnen. Auf nem PC würden ein paar Byte mehr oder weniger
niemanden interessieren, und ich käme auch nicht auf die Idee, mit den
erzeugten Code anzuschauen.
Rolf Magnus schrieb:> Johann L. schrieb:>>>> Die meisten C++-Programmierer sind es nur gewöhnt, skalare Typen per>>> Kopie und Klassen per Referenz zu übergeben, deshalb macht so eine>>> Stelle im Code immer etwas hellhörig.>>>> Anders geht das doch nicht, oder? Jedenfalls erlaubt C++ es nicht,>> Opertoren für Skalare zu überladen.>> Den Zusammenhang verstehe ich jetzt nicht. Außerdem kann man durchaus> auch Skalare als Operanden haben. Es dürfen nur nicht alle Operanden> Skalare sein.
Das wäre dann aber der Fall, wenn man zB zwei Fixpunkt-Werte
multiplizieren will. Da muss dann extra eine Klasse her, obwohl die nur
einen 8-Bit Wert beinhaltet. Und die Funktionen hatte ich so
hingeschrieben wie ich es auf nem AVR für 8-Bit Werte auch mache,
nämlich direkt übergeben und nicht per Zeiger (oder Referenz). Für diese
Multiplikation käme dann eine fmuls-Sequenz als Inline Asm in den
Operator.
Leider ist C++ keine Obermenge von C (bzw. GNU-C) und es wären sehr viel
Änderungen in der Quelle notwendig, die nichts mit diesen Operationen zu
tun haben :-(
Johann L. schrieb:> Das wäre dann aber der Fall, wenn man zB zwei Fixpunkt-Werte> multiplizieren will.
Irgendetwas muss eine konstante Bedeutung haben. Wenn du eine neue
int-int Rechnerei einführst, woher soll der Compiler dann wissen, wo
überall diese anzuwenden ist? Soll er zb für Additionen bzw.
Multiplikationen die im Zusammenhang mit Array Indizierung auftauchen
ebenfalls deine neue 'Arithmetik' benutzen?
In C++ ist es nun mal das Mittel einer Klasse, sich neue Objekte mit
neuen Eigenschaften zu bauen.
> Da muss dann extra eine Klasse her, obwohl die nur> einen 8-Bit Wert beinhaltet.
Macht ja nichts.
Ist ausser ein wenig Schreibarbeit kein großes Ding. Dafür hast du dann
aber auch einen Datentyp, den du beim Namen nennen kannst.
> Und die Funktionen hatte ich so> hingeschrieben wie ich es auf nem AVR für 8-Bit Werte auch mache,> nämlich direkt übergeben und nicht per Zeiger (oder Referenz).
Gewöhn dir für Klassenargumente gleich von vorne herein an: Übergeben
wird im Normalfall eine Referenz oder eine const Referenz. Damit
ermöglichst du den Compiler die besten Optimierungsmöglichkeiten.
Überhaupt im Zusammenspiel mit inline Funktionen.
> Leider ist C++ keine Obermenge von C (bzw. GNU-C) und es wären sehr viel> Änderungen in der Quelle notwendig, die nichts mit diesen Operationen zu> tun haben :-(
Huch?
Neue Klasse einführen
Operatoren definieren
Verwenden
Anstelle von uint8_t oder int8_t verwendest du dann deine
Fixedpoint-Klasse. Soo groß ist der Umstellungsaufwand dann meistens
auch wieder nicht.
Karl heinz Buchegger schrieb:> Johann L. schrieb:>>> Das wäre dann aber der Fall, wenn man zB zwei Fixpunkt-Werte>> multiplizieren will.>> Irgendetwas muss eine konstante Bedeutung haben. Wenn du eine neue> int-int Rechnerei einführst, woher soll der Compiler dann wissen, wo> überall diese anzuwenden ist? Soll er zb für Additionen bzw.> Multiplikationen die im Zusammenhang mit Array Indizierung auftauchen> ebenfalls deine neue 'Arithmetik' benutzen?
Momentan habe ich in der C-Quelle ein
1
typedefint8_tfrac8_t;
was allerdings nur dazu dient, beim Durchlesen der Quelle direkt zu
sehen, wie so ein Ding verwendet wird. (Ok, ging auch ungarisch, mag ich
aber net.) Immerhin geht ja sowas wie
1
typedefstruct
2
{
3
signedcharv;
4
}S;
5
6
Soperator+(Sa,Sb)
7
{
8
return(S){a.v+b.v};
9
}
und da wär es naheliegend, das auch für nen normalen Typedef zu haben.
Aber wenn es ohne Overhead möglich ist, Klassen zu verwenden, finde ich
die Schreibarbeit, die mit Klassenbildung einhergeht, kein Drama.
>> Und die Funktionen hatte ich so>> hingeschrieben wie ich es auf nem AVR für 8-Bit Werte auch mache,>> nämlich direkt übergeben und nicht per Zeiger (oder Referenz).>> Gewöhn dir für Klassenargumente gleich von vorne herein an: Übergeben> wird im Normalfall eine Referenz oder eine const Referenz. Damit> ermöglichst du den Compiler die besten Optimierungsmöglichkeiten.> Überhaupt im Zusammenspiel mit inline Funktionen.
Bei avr-g++ hab ich noch überhaupt kein Gefühl dafür, wie der tickt und
wo seine Haken und Ösen sind. Auf nem PC hätt ich da wie gesagt keine
Bauchschmerzen und der Code der rauskommt ist mir ziemlich wurscht.
Zumindest kommt's da nicht auf ein paar Bytes mehr oder weniger an.
>> Leider ist C++ keine Obermenge von C (bzw. GNU-C) und es wären sehr viel>> Änderungen in der Quelle notwendig, die nichts mit diesen Operationen zu>> tun haben :-(>> Huch?> Neue Klasse einführen> Operatoren definieren> Verwenden
Ich meine Sachen wie
1
#include<avr/pgmspace.h>
2
3
enum
4
{
5
OBJ_1,
6
OBJ_2,
7
OBJ_3,
8
...
9
};
10
11
typedefstruct
12
{
13
int8_ta;
14
int8_tb;
15
...
16
}foo_t;
17
18
constfoo_tfoo[]PROGMEM=
19
{
20
[OBJ_1]=
21
{
22
.a=1,
23
.b=2,
24
...
25
},
26
27
[OBJ_2]=
28
{
29
.a=-1,
30
.b=5,
31
...
32
},
33
34
[OBJ_3]=
35
....
36
};
Die Designators von GNU-C sind zwar nicht notwendig, ich find einen
Initialiser damit aber wesentlich besser lesbar als einen
Spaghetti-Initialiser, wo man immer abzählen muss. Wenn man nen recht
langen Initialiser hat, hat man sich schnell mal verzählt. Ausserdam ist
es damit bei eine Typänderung wesentlich einfacher, die Initialiser
konstant zu halten.
Wenn ich's recht sehe, gibt es sowas wie Designators in C++ nicht?
Ausserdem kann man offenbar keine Objekte ins Flash legen und von dort
aus einlesen. Gibt eine Warnung von avr-g++ auch wenn der erzeugt Code
ok ist. Ist wohl ein Fall der im AVR-Backend von gcc nicht abgehandelt
wird. Die Warnung zum Attribut pgmspace kommt ja von dort.
D.h. wenn die Quelle ohne Warnungen übersetzt werden soll, dann muss man
jede Klasse 2x anlegen: einmal als Klasse und einmal als Struktur, wobei
letzte ins Flash gelegt werden kann um die Klasse daraus zu
deserialisieren.
Johann L. schrieb:> und da wär es naheliegend, das auch für nen normalen Typedef zu haben.
Ein Typedef ist nur ein Alias. Also ein anderer Name. Nach
1
typedefintmy_int;
ist my_int in allen Belangen völlig gleichwertig mit einem int.
Leider.
>> Gewöhn dir für Klassenargumente gleich von vorne herein an: Übergeben>> wird im Normalfall eine Referenz oder eine const Referenz. Damit>> ermöglichst du den Compiler die besten Optimierungsmöglichkeiten.>> Überhaupt im Zusammenspiel mit inline Funktionen.>> Bei avr-g++ hab ich noch überhaupt kein Gefühl dafür, wie der tickt und> wo seine Haken und Ösen sind.
Das hat überhaupt nichts mit dem gcc zu tun, sondern mit C++ an sich.
Eine Referenz ist ganz einfach nur "ein anderer Name für ein an sonsten
existierendes Objekt".
Damit darf der Compiler überall dort, wo er eine Referenz hat auch das
originale Objekt benutzen, wenn er an es rankommt.
> Auf nem PC hätt ich da wie gesagt keine> Bauchschmerzen
genau so sehen dann auch viele C++ Programme aus und alle Welt schreit:
Memory Management in C++ ist so schwierig, wir brauchen unbedingt
Reference-counted garbage collected Klassen.
> Wenn ich's recht sehe, gibt es sowas wie Designators in C++ nicht?
Noch nicht.
Karl heinz Buchegger schrieb:> Damit darf der Compiler überall dort, wo er eine Referenz hat auch das> originale Objekt benutzen, wenn er an es rankommt.
Oder genauer: er wird über die Referenz immer das originale Objekt
benutzen; es gibt da keine Wahlfreiheit.
Johann L. schrieb:> Momentan habe ich in der C-Quelle ein> typedef int8_t frac8_t;>> was allerdings nur dazu dient, beim Durchlesen der Quelle direkt zu> sehen, wie so ein Ding verwendet wird.
Mehr geht mit einem Typedef auch nicht.
> Immerhin geht ja sowas wie>> typedef struct> {> signed char v;> } S;>> S operator+ (S a, S b)> {> return (S) { a.v + b.v };> }>> und da wär es naheliegend, das auch für nen normalen Typedef zu haben.
Das ist auch ein "nomaler Typedef", den du halt nur mit einer
Klassendefinition verwurstet hast.
Du definierst hier auf etwas umständliche Art eine Struktur mit Namen
S, indem du erst eine namenlose Struktur erzeugst und dann über Typedef
ihr den "alternativen" Namen S gibst und diese beiden Aktionen zu einer
kombinierst. Es ist also eine verkürzte Schreibeweise von ungefähr dem:
1
structNamenlos
2
{
3
signedcharv;
4
};
5
6
typedefNamenlosS;
Aber den Typedef könnte man sich auch sparen und stattdessen gleich
schreiben:
1
structS
2
{
3
signedcharv;
4
};
Der Effekt wäre derselbe.
Der Typedef verhält sich hier also auch nicht anders als beim int8_t.
Der Unterschied, weshalb du hier einen Operator definieren kannst, hat
also nichts damit zu tun, daß der Typedef nicht "normal" wäre, sondern
damit, daß der im einen Fall auf einen skalaren Typ verweist, im anderen
auf eine Klasse.
> Aber wenn es ohne Overhead möglich ist, Klassen zu verwenden, finde ich> die Schreibarbeit, die mit Klassenbildung einhergeht, kein Drama.
Das ist größtenteils durchaus möglich.
> Die Designators von GNU-C sind zwar nicht notwendig, ich find einen> Initialiser damit aber wesentlich besser lesbar als einen> Spaghetti-Initialiser, wo man immer abzählen muss. Wenn man nen recht> langen Initialiser hat, hat man sich schnell mal verzählt.
Zur Not könnte man das auch mit Kommentaren machen.
> Ausserdam ist es damit bei eine Typänderung wesentlich einfacher, die> Initialiser konstant zu halten.
Warum?
> Wenn ich's recht sehe, gibt es sowas wie Designators in C++ nicht?
Nein.
> Ausserdem kann man offenbar keine Objekte ins Flash legen und von dort> aus einlesen. Gibt eine Warnung von avr-g++ auch wenn der erzeugt Code> ok ist.
Was denn für eine?
>> Der Effekt wäre derselbe.
Nicht, wenn man schreibfaul ist :-)
>> Die Designators von GNU-C sind zwar nicht notwendig, ich find einen>> Initialiser damit aber wesentlich besser lesbar als einen>> Spaghetti-Initialiser, wo man immer abzählen muss. Wenn man nen recht>> langen Initialiser hat, hat man sich schnell mal verzählt.>> Zur Not könnte man das auch mit Kommentaren machen.>>> Ausserdam ist es damit bei eine Typänderung wesentlich einfacher, die>> Initialiser konstant zu halten.>> Warum?
Typo. Soll heissen
>> Ausserdam ist es damit bei eine Typänderung wesentlich einfacher, die>> Initialiser konsistent zu halten.>> ^^^^^^^^^^
D.h. wenn in der Entwicklungsphase Felder hinzukommen / verschwinden
etc. Ein Kommentar ist ja kein Teil der Semantik, ein Designator schon.
>> Wenn ich's recht sehe, gibt es sowas wie Designators in C++ nicht?>> Nein.>>> Ausserdem kann man offenbar keine Objekte ins Flash legen und von dort>> aus einlesen. Gibt eine Warnung von avr-g++ auch wenn der erzeugt Code>> ok ist.>> Was denn für eine?
1
classV
2
{
3
public:
4
5
signedcharv;
6
7
inlineV(){};
8
inlineV(signedcharvalue):v(value){};
9
};
10
11
#include<avr/pgmspace.h>
12
13
constuint8_tq[]PROGMEM=
14
{
15
0x12,0x11
16
};
17
18
constVw[]PROGMEM=
19
{
20
V(0x12)
21
};
22
23
Vread(void)
24
{
25
returnV(pgm_read_byte(&q));
26
}
1
warning: only initialized variables can be placed into program memory area
2
warning: only initialized variables can be placed into program memory area
3
error: q causes a section type conflict
Und auch ohne w gibt's ne Warnung für q, wennglaich auch keinen
irreführenden Fehler.
Johann L. schrieb:>> Der Effekt wäre derselbe.>> Nicht, wenn man schreibfaul ist :-)
Stimmt. Das 'typedef' muß man bei deiner Variante zusätzlich tippen. Das
ist dann aber auch der einzige Unterschied.
> warning: only initialized variables can be placed into program memory area
Ah. Ich schätze, das kommt daher, daß es sich um dynamische
Initialisierung handelt. Während q nämlich direkt mit einer Konstante
initialisiert wird, wird das bei w durch einen Funktionsaufruf
(Konstruktor) gemacht, und (auch wenn's komisch klingt) das würde
bedeuten, daß der Konstruktor den Flash beschreiben können müßte. In
diesem Fall könnte das zwar auch wegoptimiert werden, weil der
Initialisierer ja auch schon im Flash steht und vom Konstruktor nur in
die Membervariable kopiert wird, aber im allgemeinen Fall geht das eben
nicht.
Rolf Magnus schrieb:> Johann L. schrieb:>>> Der Effekt wäre derselbe.>>>> Nicht, wenn man schreibfaul ist :-)>> Stimmt. Das 'typedef' muß man bei deiner Variante zusätzlich tippen. Das> ist dann aber auch der einzige Unterschied.
Nö. Ansonsten muss bei jeder Definition/Deklaration/Cast ein
struct/union hinzu.
>> warning: only initialized variables can be placed into program memory area>> Ah. Ich schätze, das kommt daher, daß es sich um dynamische> Initialisierung handelt. Während q nämlich direkt mit einer Konstante> initialisiert wird, wird das bei w durch einen Funktionsaufruf
Die Warnung kommt auch bei
1
classV
2
{
3
public:
4
signedcharv;
5
inlineV(){};
6
inlineV(signedcharvalue):v(value){};
7
};
8
9
#include<avr/pgmspace.h>
10
11
constuint8_tq[]PROGMEM=
12
{
13
0x12,0x11
14
};
15
16
Vread(void)
17
{
18
returnV(pgm_read_byte(&q));
19
}
> (Konstruktor) gemacht, und (auch wenn's komisch klingt) das würde> bedeuten, daß der Konstruktor den Flash beschreiben können müßte. In> diesem Fall könnte das zwar auch wegoptimiert werden, weil der> Initialisierer ja auch schon im Flash steht und vom Konstruktor nur in> die Membervariable kopiert wird, aber im allgemeinen Fall geht das eben> nicht.
Mit dem Initializer für w (der V() verwendet) kommt ja auch wie erwartet
ein Fehler, aber der beschwert sich über q !
Johann L. schrieb:>> Stimmt. Das 'typedef' muß man bei deiner Variante zusätzlich tippen. Das>> ist dann aber auch der einzige Unterschied.> Nö. Ansonsten muss bei jeder Definition/Deklaration/Cast ein> struct/union hinzu.
In C++? Nein. "struct foo;" deklariert in C++ einen Klassen-Typ namens
"foo", der ohne "struct" davor verwendet werden kann. Dasselbe gilt auch
für "union". Im Gegenteil, es ist eine explizite Ausnahme im Standard
nötig, damit ein "typedef struct foo foo;" zu keinem Fehler führt (die
Ausnahme ist wohl im wesentlichen der Verwendbarkeit von C-Headern
geschuldet). Ein "struct foo; typedef int foo;" liefert in C++ einen
Fehler, während es in C erlaubt wäre.
Andreas
Johann L. schrieb:> Rolf Magnus schrieb:>> Johann L. schrieb:>>>> Der Effekt wäre derselbe.>>>>>> Nicht, wenn man schreibfaul ist :-)>>>> Stimmt. Das 'typedef' muß man bei deiner Variante zusätzlich tippen. Das>> ist dann aber auch der einzige Unterschied.>> Nö. Ansonsten muss bei jeder Definition/Deklaration/Cast ein> struct/union hinzu.
Ähm. nein.
In C: ja
In C++: nein
@Rolf Magnus:
>Er verändert keinen Operanden, auch wenn das ein Java-Programmierer>nicht erwarten würde, weil der den Unterschied zwischen call-by-value>und call-by-reference nicht kennt. ;-)
Ich bin mit C++ groß geworden und erst später in Java eingestiegen. ;-)
Allerdings sehe ich in:
>static inline V operator+ (V a, const V b)>{> a.v += b.v;> return a;>}
keine Referenz, und außerdem bleibe ich dabei: die Methode verändert a,
was laut Kontrakt des Additionsoperators nicht sein sollte. Außerdem
liefert er dann (das veränderte) a als Ergebnis, was ebenfalls nicht
sein sollte.
Oder habe ich irgend etwas Wichtiges übersehen?
High Performer schrieb:> @Rolf Magnus:>>>Er verändert keinen Operanden, auch wenn das ein Java-Programmierer>>nicht erwarten würde, weil der den Unterschied zwischen call-by-value>>und call-by-reference nicht kennt. ;-)>> Ich bin mit C++ groß geworden und erst später in Java eingestiegen. ;-)> Allerdings sehe ich in:>>>static inline V operator+ (V a, const V b)>>{>> a.v += b.v;>> return a;>>}>> keine Referenz,
Ja, eben. Genau deshalb kann der Operator auch den Operanden gar nicht
verändern. Er wurde "by value" übergeben und nicht per Referenz. Der
Operator verändert lediglich eine lokale Kopie. Man könnte das natürlich
auch selber machen, indem man den Parameter als Referenz definiert und
dann innerhalb der Funktion selbst eine Kopie anlegt, aber das ist
umständlicher und hat keinen Vorteil.
> und außerdem bleibe ich dabei: die Methode verändert a,
Sie verändert a, aber a ist nicht der übergebene Operand, sondern eine
Kopie davon.
> was laut Kontrakt des Additionsoperators nicht sein sollte.
Wenn er nicht veränderbar sein soll, mußt du ihn als const definieren.
Aber außerhalb der Funktion hätte das keinerlei Auswirkung.
@Rolf:
Du hast natürlich vollkommen Recht! Ich sah mal wieder den Wald vor
lauter Bäumen nicht. Hänge gerade wohl doch ein wenig zu sehr in Java
drin. ;-)
Mea culpa!