Ein typedef ist nur eine Typvereinbarung.
Wenn Du eine Variable dieses Typs anlegst, kannst Du sie entweder so
initialisieren, wie man das mit dem zugrundelegenden Typ macht (in
Deinem Fall also einer Initialisierungsliste für Deine Struktur), oder
aber, Du kannst jedes einzelne Byte, aus dem die Variable besteht, auf 0
setzen:
DerEgon schrieb:> Ein typedef ist nur eine Typvereinbarung.
Nein, ein typedef ist ein Typ-Alias. Entgegen dem Namen deklariert
typedef keinen distinkten DT.
Wilhelm M. schrieb:> Nein, ein typedef ist ein Typ-Alias. Entgegen dem Namen deklariert> typedef keinen distinkten DT.
Ein typedef verhält sich aber wie ein Typ, insofern ist die
Unterscheidung ziemlich irrelevant.
DerEgon schrieb:> Wilhelm M. schrieb:>> Nein, ein typedef ist ein Typ-Alias. Entgegen dem Namen deklariert>> typedef keinen distinkten DT.>> Ein typedef verhält sich aber wie ein Typ, insofern ist die> Unterscheidung ziemlich irrelevant.
Nö, das ist sehr relevant. Aber für Programmierer für die alles ein int
ist, wenn es kein String sein kann, magst Du Recht haben ...
In der Sprachsyntax von C ist etwas wie die Typedef-Namen eigentlich
überhaupt nicht sauber möglich. Einer der Geburtsfehler der Syntax, denn
ursprünglich gab es keine typedefs.
Denn die üblichen Typnamen wie "int" sind Keywords, und das nicht ohne
Grund. Weshalb Typedef-Namen ebenfalls die Rolle von Keywords übernehmen
müssen, aber natürlich nur so halb, andernfalls wären sie beispielsweise
nicht als Strukt-Namen möglich. Ja nach syntaktischem Kontext werden
Typedef-Namen vom Lexer als quasi-Keywords oder als normale Identifier
betrachtet.
Wer Sinn für formale Syntax hat, kriegt bei sowas die Krise. Aber so ist
es eben und man lebt damit.
test1 akzeptiert der Compiler klaglos, weil er zwei ints erwartet und
zwei bekommt. Es sind eben nur Aliasse und keine neuen Typen. In test2
wehrt sich der Compiler, weil er nicht die richtigen Typen bekommt.
Wilhelm M. schrieb:> Ich hätte es so gezeigt:1typedef char CC;> 23static_assert(std::is_same_v<char, CC>);
War klar, ist aber wie immer für alle außerhalb der „++“-Welt nicht
geeignet.
Oliver
Oliver S. schrieb:> Wilhelm M. schrieb:>> Ich hätte es so gezeigt:1typedef char CC;>> 23static_assert(std::is_same_v<char, CC>);>> War klar, ist aber wie immer für alle außerhalb der „++“-Welt nicht> geeignet.
Im Eingangspost steht nichts von "nur C".
Zorro schrieb:> WIe kann ich eine typedef struct das aus mehrern typedef structs besteht> alle Variablen auf 0 initialisieren?
structs in structs werden mit nested {} initialisiert.
Du kannst in C alle "folgenden" Elemente weglassen, die mit 0
initialisiert werden sollen.
ja, ein typedef hat nichts mit structs zu tun. Ein typedef verschleiert
da nur / spart tipparbeit.
Beispiel ohne typedef.
A. S. schrieb:> structs in structs werden mit nested {} initialisiert.
Ich würde designated Initializer bevorzugen. Falls irgendjemand die
Reihenfolge der Struct umstellt fliegt man da sonst relativ schnell auf
die Schnauze und hat auch viel Tipparbeit alle Initialisierungslisten
umzustellen.
MaNi schrieb:> Falls irgendjemand die
Namen
> der Struct umstellt fliegt man da sonst relativ schnell auf> die Schnauze und hat auch viel Tipparbeit alle Initialisierungslisten> umzustellen.
A. S. schrieb:> Namen
Ne ich meinte schon die Reihenfolge.
Bei einer normalen initialisieren ist die Zuweisung der Werte von der
Reihenfolge der Definition abhängig.
Bei Designated Initialisation nicht mehr.
MaWin schrieb:> Die Nullen kannst du dir sparen.
Nein, das ist ub. Eine reicht aber, T x={0}; geht immer.
Irgendwann kommt noch c23 raus, dort darf man es dann endlich.
MaWin schrieb:> DPA schrieb:>> Nein, das ist ub.>> Unfug
Ja, sorry, ich korrigiere mich, es ist nicht nur UB, es ist ein syntax
Fehler. Hier ist der c11 draft:
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1516.pdf
Unter 6.7.9 Initialization, die formale Syntaxbeschreibung lässt es
nicht zu.
Und zu c23 https://en.m.wikipedia.org/wiki/C2x> Changes integrated into the latest working draft are: ... Zero initialization
with {}
N. M. schrieb:> Bei Designated Initialisation nicht mehr.
Ja. Nur sind dann Umbenennungen aufwendig.
Da struct-member meist kurze Namen haben (nicht unique), geht
suchen/ersetzen nicht.
Ich gruppiere praktisch nie um und erweitere nur am Ende. Umbenennungen
dagegen jederzeit, wenn es zur Klarheit beiträgt.
Ich komme allerdings aus einer Zeit, wo designated nicht möglich war.
Sonst hätte ich es vielleicht immer so gemacht.
A. S. schrieb:> Ja. Nur sind dann Umbenennungen aufwendig.> Da struct-member meist kurze Namen haben (nicht unique), geht> suchen/ersetzen nicht.
Bessere IDEs bieten hier ein Rename Feature an die nicht nur den Namen
sondern den Typ berücksichtigen beim suchen.
Sollte man dann trotzdem einen vergessen haben sieht man spätestens beim
compileren wo der vergessene steckt, da es dann ja ein Compiler Error
gibt.
A. S. schrieb:> Ich komme allerdings aus einer Zeit, wo designated nicht möglich war.> Sonst hätte ich es vielleicht immer so gemacht.
Das ist schon seit C99 drin 😄
Aber klar, jeder wie er möchte.
MaWin schrieb:> Komisch, dass es mit GCC seit Jahrzehnten geht.
Nicht komisch, sondern für GCC in Standardeinstellung normal. GCC ist
u.A. eine Art Testplattform für neue Entwicklungen der Sprache(n).
Probiers mal mit -pedantic.
MaWin schrieb:> (prx) A. K. schrieb:>> Nicht komisch, sondern für GCC in Standardeinstellung normal.>> Na dann ist ja alles gut und der Hinweis über UB war Quatsch.
Nein, das war kein quatsch. Leere initializer listen sind, momentan,
schlicht kein gültiges C. Das GCC nicht abbricht, selbst wenn man ihm
sagt sich an einen standard zu halten, ist eigentlich ein Fehler. Andere
implementationen könnten/sollten da auch auf die schnauze fallen. Aber
es ist eine kleine Erweiterung, und es verwirrt die user, wenn sie das
nicht machen können, die denken dann, es wäre ein bug. Deshalb
akzeptieren die meisten Compiler das halt trotzdem, es schadet ja
keinem.
DPA schrieb:> Das GCC nicht abbricht, selbst wenn man ihm> sagt sich an einen standard zu halten, ist eigentlich ein Fehler.
So ein Käse.
Ich gab hier lediglich einen praktischen Hinweis, der einem das Leben
erleichtert.
Ich (und viele viele andere Entwickler) verwende diesen "Fehler" schon
seit Jahrzehnten.
> es schadet ja keinem.
Na dann.
MaWin schrieb:> Ich (und viele viele andere Entwickler) verwende diesen "Fehler" schon> seit Jahrzehnten.
Ja, du (und viele andere), können halt kein C, wie man sieht.
(prx) A. K. schrieb:> In der Sprachsyntax von C ist etwas wie die Typedef-Namen eigentlich> überhaupt nicht sauber möglich. Einer der Geburtsfehler der Syntax, denn> ursprünglich gab es keine typedefs.>> Denn die üblichen Typnamen wie "int" sind Keywords, und das nicht ohne> Grund.
Ja, weil sie in der Sprache fest eingebaut sind.
> Weshalb Typedef-Namen ebenfalls die Rolle von Keywords übernehmen> müssen, aber natürlich nur so halb, andernfalls wären sie beispielsweise> nicht als Strukt-Namen möglich.
Die Argumentation verstehe ich nicht. Eine Sprache, in der alle Typnamen
Keywords sind, ist ziemlich limitiert.
> Ja nach syntaktischem Kontext werden Typedef-Namen vom Lexer als quasi-> Keywords oder als normale Identifier betrachtet.
Was ist ein quasi-Keyword, und wo werden typedef-Namen so behandelt?
> Wer Sinn für formale Syntax hat, kriegt bei sowas die Krise.
Sieht für mich jetzt eigentlich nach etwas aus, das so ziemlich in jeder
ernstzunehmenden Programmiersprache so ist.
Wilhelm M. schrieb:> Im Eingangspost steht nichts von "nur C".
Da der TE aber von "typedef structs" spricht, ist eher davon auszugehen.
A. S. schrieb:> N. M. schrieb:>> Bei Designated Initialisation nicht mehr.>> Ja. Nur sind dann Umbenennungen aufwendig.>> Da struct-member meist kurze Namen haben (nicht unique), geht> suchen/ersetzen nicht.
Dafür haben Editoren doch heute eine Funktion, die das semantikbasiert
über den kompletten Code hinweg macht.
> Ich gruppiere praktisch nie um und erweitere nur am Ende. Umbenennungen> dagegen jederzeit, wenn es zur Klarheit beiträgt.
Bei mir ist es gerade umgekehrt. Elemente einer struct nenne ich nur
sehr selten um (höchstens mal, um einen Tippfehler zu korrigieren), aber
dass mal zwischen zwei bestehenden Elementen ein neues hinzukommt statt
nur am Ende, das kann schon mal vorkommen. Oder auch, dass ein nicht
mehr benötigtes entfernt wird. Sowas nur deshalb drin (und dann
ungenutzt) zu lassen, damit sich das Layout der Struktur nicht ändert,
finde ich nicht sonderlich elegant.
> Ich komme allerdings aus einer Zeit, wo designated nicht möglich war.> Sonst hätte ich es vielleicht immer so gemacht.
Ich komme auch aus dieser Zeit, aber ich hab halt dazugelernt. ;-)
MaWin schrieb:> Ich (und viele viele andere Entwickler) verwende diesen "Fehler" schon> seit Jahrzehnten.
Das macht es nicht automisch zu korrektem C.
Rolf M. schrieb:> Das macht es nicht automisch zu korrektem C.
Leute, Leute.
Es war ein praktischer und gut gemeinter Hinweis.
Als Antwort kam dann, dass es UB sei. Was völliger Unsinn ist.
Und dann kam die Ausflucht, dass es Syntax Error sei. Was auch Unsinn
ist, bei den meisten gängigen Compilern.
Soll der Threadersteller doch selbst entscheiden, ob er es nutzen will.
Falsche Behauptungen (UB) helfen ihm dabei sicher nicht.
Rolf M. schrieb:>> Denn die üblichen Typnamen wie "int" sind Keywords, und das nicht ohne>> Grund.>> Ja, weil sie in der Sprache fest eingebaut sind.
Keyword = reservierter Bezeichner, der eine prägende Rolle in der Syntax
der Sprache spielt.
>> Ja nach syntaktischem Kontext werden Typedef-Namen vom Lexer als quasi->> Keywords oder als normale Identifier betrachtet.>> Was ist ein quasi-Keyword, und wo werden typedef-Namen so behandelt?
In der lexikalischen Analyse, also der Zerlegung des Quelltextes in
syntaktische Elemente, wird zwischen reservierten Wörtern wie "int" und
anderen Namen unterschieden. Der Lexer wirft also bei "int" das Token
INT aus, bei normalen Namen aber NAME.
In der Syntax von C wird der Beginn einer Deklaration/Definition durch
solche Keyword-Token wie "extern" oder "int" erkannt. Das war anno
Sprachursprung syntaktisch sauber.
Mit Typedefs wurde es haarig, denn die haben ja syntaktisch exakt die
gleiche Rolle wie obige Keywords, sind aber keine. Obendrein gibts sowas
wie
typedef struct s *s; // incomplete
struct s { s p; ... }; // completion
worin s zwei verschiedene lexikalische Rollen hat. In der zweiten Zeile
muss der Lexer abhängig vom syntaktischen Kontext des Parsers den
gleichen Namen s wahlweise in TYPENAME oder NAME auflösen:
STRUCT //expect=name// NAME '{' //expect=type// TYPENAME NAME ';' ...
Man muss also im Parser die Arbeitsweise des Lexers umschalten, abhängig
vom syntaktischen Kontext. Und das ist eine zwar machbare, aber
konzeptionell unsaubere Sache.
Obacht Falle: Ich beziehe mich hier nicht auf verschiedene Namespaces
innerhalb normaler Identifier, wie etwa dem getrennten Namespace der
Namen von structs. Das ist ein völlig anderes Thema.
> Sieht für mich jetzt eigentlich nach etwas aus, das so ziemlich in jeder> ernstzunehmenden Programmiersprache so ist.
Keineswegs. Bei einer Syntax wie
TYPE t: ...;
VAR i: t;
sind nur TYPE und VAR Keywords. Der Typname t ist ein ganz gewöhnlicher
Name, ohne jedwede Sonderrolle.
MaWin schrieb:> Als Antwort kam dann, dass es UB sei. Was völliger Unsinn ist.> Und dann kam die Ausflucht, dass es Syntax Error sei. Was auch Unsinn> ist, bei den meisten gängigen Compilern.
Kein unsinn, sondern tatsache. Was nicht dem Standard entspricht, ist
kein C, was der Compiler schluckt ist da irrelevant.
(prx) A. K. schrieb:> Keyword = reservierter Bezeichner, der eine prägende Rolle in der Syntax> der Sprache spielt.
Ja, genau deshalb sind die einebauten Typen Schlüsselwörter.
>> Was ist ein quasi-Keyword, und wo werden typedef-Namen so behandelt?>> In der lexikalischen Analyse, also der Zerlegung des Quelltextes in> syntaktische Elemente, wird zwischen reservierten Wörtern wie "int" und> anderen Namen unterschieden. Der Lexer wirft also bei "int" das Token> INT aus, bei normalen Namen aber NAME.
Das ist mir bewusst, nennt man in der Regel aber eher Identifier. Der
ist aber kein "pseudo-Keyword".
Da wäre eher noch die umgekehrte Betrachtungsweise angebracht, dass
Keywords eine Art pseudo-Idendifier sind, weil sie auch der
lexikalischen Regel für Identifier entsprechen würden, aber eine
besondere Rolle spielen.
> In der Syntax von C wird der Beginn einer Deklaration/Definition durch> solche Keyword-Token wie "extern" oder "int" erkannt. Das war anno> Sprachursprung syntaktisch sauber.>> Mit Typedefs wurde es haarig, denn die haben ja syntaktisch exakt die> gleiche Rolle wie obige Keywords, sind aber keine.
Syntaktisch haben sie die Rolle eines type-specifier. Die Syntax von C
ist im Standard definiert und sieht an der Stelle so aus:
1
type-specifier:
2
void
3
char
4
short
5
int
6
long
7
float
8
double
9
signed
10
unsigned
11
_Bool
12
_Complex
13
struct-or-union-specifier
14
enum-specifier
15
typedef-name
Und ein typedef-name ist dann schließlich ein Identifier.
> Obendrein gibts sowas> wie> typedef struct s *s; // incomplete> struct s { s p; ... }; // completion> worin s zwei verschiedene lexikalische Rollen hat. In der zweiten Zeile> muss der Lexer abhängig vom syntaktischen Kontext des Parsers den> gleichen Namen s wahlweise in TYPENAME oder NAME auflösen:
Nein, der Lexer löst das einfach als Identifier mit Text "s" auf, und
das gibt er an den nachgeschalteten Parser weiter. Der erkennt dann
anhand der passendend Parser-Regel, auf was sich der Identifier an der
jeweiligen Stelle beziehen muss.
>> Sieht für mich jetzt eigentlich nach etwas aus, das so ziemlich in jeder>> ernstzunehmenden Programmiersprache so ist.>> Keineswegs. Bei einer Syntax wie> TYPE t: ...;> VAR i: t;> sind nur TYPE und VAR Keywords. Der Typname t ist ein ganz gewöhnlicher> Name, ohne jedwede Sonderrolle.
Und eingebaute Typen hat diese Sprache dann nicht?
Rolf M. schrieb:> Nein, der Lexer löst das einfach als Identifier mit Text "s" auf, und> das gibt er an den nachgeschalteten Parser weiter.
Ich erinnere mich sowohl an die von mir beschriebene Variante (uralt),
finde aber auch einen aktuellen Lexer, der diesen Teil der
Kontextabhängigkeit selbst erledigt.
Nach "notype" suchen. Liefert entweder C_NAME oder C_TYPENAME:
https://github.com/IanHarvey/pcc/blob/master/cc/ccom/scan.l
Rolf M. schrieb:> Und eingebaute Typen hat diese Sprache dann nicht?
Deren Namen sind keine Keywords / reserved Words, sondern lediglich
vordefinierte normale Identifier. In Pascal ist VAR reserviert, INTEGER
aber ein gewöhnlicher Identifier.
(prx) A. K. schrieb:> Ich erinnere mich sowohl an die von mir beschriebene Variante (uralt),> finde aber auch einen aktuellen Lexer, der diesen Teil der> Kontextabhängigkeit selbst erledigt.
Hat das denn einen speziellen Vorteil? Es scheint mir unnötig
umständlich, in den Lexer eine Kontextabhängigkeit einzubauen, die man
eigentlich nicht unbedingt bräuchte.
(prx) A. K. schrieb:> Rolf M. schrieb:>> Und eingebaute Typen hat diese Sprache dann nicht?>> Deren Namen sind keine Keywords / reserved Words, sondern lediglich> vordefinierte normale Identifier. In Pascal ist VAR reserviert, INTEGER> aber ein gewöhnlicher Identifier.
Ok, das kann man machen. Könnte man dann eine Variable oder Funktion
definieren, die INTEGER heißt?
Rolf M. schrieb:> Ok, das kann man machen. Könnte man dann eine Variable oder Funktion> definieren, die INTEGER heißt?
Syntaktisch wäre das kein Problem. Nachschlagen darfst du selbst. ;-)
Am deutlichsten wird die Problematik von typedef in folgendem Beispiel:
1
x*y;
Im Kontext
1
intx,y;
ist es ein Produkt, dessen Ergebnis nicht weiter verwendet wird.
Im Kontext
1
typedefintx;
hingegen wird damit y als Zeiger auf int deklariert.
Das sind zwei völlig verschiedene Dinge, die weder mittels regulärer
Ausdrücke (für den Lexer) noch mittels EBNF (für den Parser)
unterschieden werden können. Man benötigt dafür also zusätzliche
Verrenkungen, die wahlweise im Lexer oder im Parser implementiert
werden. Elegant ist das aber in beiden Fällen nicht.
DPA schrieb:> Leere initializer listen sind, momentan, schlicht kein gültiges C.
So ist es.
> Das GCC nicht abbricht, selbst wenn man ihm sagt sich an einen> standard zu halten, ist eigentlich ein Fehler.
Mit -pedantic-errors weist man den Compiler an, sich strikt an den
Standard zu halten. Dann gibt er erwartungsgemäß auch eine entsprechende
Fehlermeldung aus:
1
test.c:1:23: error: ISO C forbids empty initializer braces [-Wpedantic]
Yalu X. schrieb:> Das sind zwei völlig verschiedene Dinge, die weder mittels regulärer> Ausdrücke (für den Lexer) noch mittels EBNF (für den Parser)> unterschieden werden können.
Dafür alleine reicht die Symboltabelle. Ergibt die im passenden Kontext,
dass "y" ein Typedef ist, gibts C_TYPENAME, sonst C_NAME. Siehe oben
verlinkten Lexer-Code.
Interessant wird es erst dann, wenn das y auch an Stellen vorkommt, wo
es kein Typname sein kann. Meine Struct war nur ein Beispiel.
1
typedefinty;
2
3
intf(void)
4
{
5
yy=y;// Lex: C_TYPENAME C_NAME '=' C_NAME
6
returny;
7
}
In C++ wirds über die Cast-Syntax noch etwas schöner:
1
typedefinty,z;
2
intk;
3
4
intf(inti)
5
{
6
yx=y(i);// OK, ist ein Cast nach Typ y
7
zz=z(i);// NOK, z() müsste eine Funktion sein
8
y(k);// wird hier k definiert, oder ist das ein nutzloser Cast?
(prx) A. K. schrieb:> Yalu X. schrieb:>> Das sind zwei völlig verschiedene Dinge, die weder mittels regulärer>> Ausdrücke (für den Lexer) noch mittels EBNF (für den Parser)>> unterschieden werden können.>> Dafür alleine reicht die Symboltabelle.
Ja, klar, nur hat in einer idealen Welt weder die lexikalische noch die
syntaktische Analyse etwas mit Symboltabellen am Hut. Dass das in der
realen Welt dennoch der Fall ist, liegt eben an dem etwas unglücklich
nachgerüsteten typedef-Konstrukt.
> y y = y;
Wäre dies das alleinige Problem, könnte man im Lexer und Parser auf den
Zugriff auf die Symboltabelle verzichten und stattdessen den Look-Ahead
des Parsers von 1 auf 2 erhöhen. Bei zwei aufeinanderfolgenden
Identifiern an dieser Stelle ist der erste IMHO immer ein Typname
(zumindest fällt mir spontan kein Gegenbeispiel ein).
Damit wäre der Lexer ein reiner Lexer, der Parser ein reiner Parser und
die Welt wieder etwas idealer :)
Aber die Mehrdeutigkeit in meinem obigen Beispiel
1
x*y;
kann auch mit einem größeren (fixen) Look-Ahead nicht aufgelöst werden,
weswegen hier tatsächlich der Zugriff auf die Symboltabelle benötigt
wird.
Yalu X. schrieb:> Am deutlichsten wird die Problematik von typedef in folgendem Beispiel:
Ok, ich sehe das Problem. Das liegt aber an sich weniger an typedef,
sondern eher an der Mehrdeutigkeit von x * y. Das ist besonders
ärgerlich, weil es als Multiplikation eigentlich keinen Sinn gibt, das
so für sich hinzuschreiben, es aber trotzdem durch die Sprache erlaubt
ist. Als Teil eines
1
z=x*y;
wäre es dann wieder eindeutig.
(prx) A. K. schrieb:> y(k); // wird hier k definiert, oder ist das ein nutzloser> Cast?
In C++ gibt's die Regel: Wenn es eine Deklaration sein könte, dann ist
es auch eine. Es wird also eine Variable namenes k vom Typ y definiert.
Das Hauptproblem war die Grundentscheidung, Datentypen, Speicherklassen
etc zu Keywords zu machen und damit Deklarationen anzufangen. Damit lag
nicht nur dieses Kind im Brunnen.
Mal ganz grundsätzlich gefragt: Wieso wird in diesem einfachen Beispiel
überhaupt typedef genutzt?
Ich nutze diese Dinger eigentlich nur für Funktionszeiger, da ist mir
die Syntax sonst zu umständlich. Aber bei einem struct oder enum
schreibe ich lieber "struct x" bzw. "enum y"...
Man kann sich das Leben unnötigerweise beliebig kompliziert machen.
Du meinst, weil es an dem Punkt noch eindeutig war?
Vereinfacht:
KEYWORD * IDENTIFIER; = Zeigerdefinition
IDENTIFIER * IDENTIFIER; = Multiplikation
Erst durch typedef kann auch IDENTIFIER * IDENTIFIER eine
Zeigerdefinition sein.
Dann ist mir klar geworden, was du gemeint hast, und ich stimme zu.
Ich finde die Diskussion sehr interessant, da ich selbst auch an der
Entwicklung der ersten Version eines (wenn auch deutlich einfacheren)
Sprachstandards teilgenommen habe, und da gab's natürlich auch einige
Diskussionen, wie man Sachen so definiert, dass sie einem später so
wenig wie möglich auf die Füße fallen. Ich bin schon gespannt, was uns
bei der Fortführung erwartet. 🙂
Lars R. schrieb:> Mal ganz grundsätzlich gefragt: Wieso wird in diesem einfachen Beispiel> überhaupt typedef genutzt?
Die letzten Beiträge? Das sind bewusst minimierte Beispiele für Syntax,
kein produktiver Code.
> Ich nutze diese Dinger eigentlich nur für Funktionszeiger
Und sonst nur "struct s" und "union u"?
(prx) A. K. schrieb:> Und sonst nur "struct s" und "union u"?
Mache ich in C auch so. Warum soll ich hinter einem typedef verstecken,
dass es eine struct ist? Gibt es einen anderen Grund als Tippfaulheit?
(prx) A. K. schrieb:> Und sonst nur "struct s" und "union u"?
Ja. Gefällt mir optisch schon deutlich besser und damit man sofort
sieht, es ist kein primitiver Datentyp und bei einer Zuweisung wird's
teuer (wegen der Kopieraktion).
(prx) A. K. schrieb:> Das Hauptproblem war die Grundentscheidung, Datentypen, Speicherklassen> etc zu Keywords zu machen und damit Deklarationen anzufangen. Damit lag> nicht nur dieses Kind im Brunnen.
Das ist gerade das, was (aus meiner Sicht) C so elegant macht.
Andere Sprachen fügen dafür Keywords wie "var"/"let"/"mut" (oder gar
"function" oder "fn") ein oder basteln sich Konstruktionen mit
Doppelpunkten. Am schlimmsten finde ich aber, dass moderne Sprachen (wie
Rust) den Datentyp erst nach dem Identifier haben "let xy : int;" (oder
so etwas in der Art). Das ist doch scheußlich!
An diesen ganzen Dingen erkenne ich einfach nur, dass man es sich schon
bei der Spezifikation der Sprache leicht machen wollte den Compiler zu
implementieren.
Noch schlimmer ist als Zuweisung ":=" zu verwenden und als Vergleich "="
- das, was man viel häufiger braucht (nämlich die Zuweisung), sollte
stets kürzer sein.
Diese ganzen modernen Sprachen kopieren Teile der Syntax der
"C-Sprachen", bauen aber gerade da, wo C wirklich Tipparbeit spart, neue
Hässlichkeiten ein.
Microsoft hat mit C# beispielsweise etwas geschaffen, was viel eher nach
"richtigem C" aussieht als beispielsweise Rust.
Lars R. schrieb:> dass moderne Sprachen (wie> Rust) den Datentyp erst nach dem Identifier haben "let xy : int;" (oder> so etwas in der Art). Das ist doch scheußlich!
Das ist einfach nur Gewöhnungs und Geschmacksacke. Ich finde die
name:typ Schreibweise mittlerweile deutlich besser.
> An diesen ganzen Dingen erkenne ich einfach nur, dass man es sich schon> bei der Spezifikation der Sprache leicht machen wollte den Compiler zu> implementieren.
Warum wäre das schlimm?
Ich finde es außerdem gut, wenn Syntax eindeutiger wird. Nicht nur für
den Compiler.
Rust hat ganz bewusst mit vielen Mehrdeutigkeiten von C/C++ aufgeräumt.
Lars R. schrieb:> Andere Sprachen fügen dafür Keywords wie "var"/"let"/"mut" (oder gar> "function" oder "fn") ein oder basteln sich Konstruktionen mit> Doppelpunkten.
Ja, und sie machen es richtig.
Ich gebe hier nicht den c-hater. Ich verwende C seit 4 Jahrzehnten, ich
kenne es recht gut, von aussen und innen. Aber ich fand den
syntaktischen Ansatz der Wirth'schen Sprachen seit damals besser. Ich
verwendete früher auch C++ häufig, obwohl mir bereits bei der ersten
Lektüre des Stroustrup an manchen Stellen Zweifel aufkamen, ob das
wirklich so hätte aussehen müssen.
Ich hatte mir auch manchen C Nachfolger angesehen, und habe erst bei
Rust den Eindruck, dass man sich ernsthaft vom überkommenen Erbe trennt.
Immer wieder fand ich sonst gute Ideen verknüpft mit alten Fehlern (etwa
in D).
Lars R. schrieb:> An diesen ganzen Dingen erkenne ich einfach nur, dass man es sich schon> bei der Spezifikation der Sprache leicht machen wollte den Compiler zu> implementieren.
Ja. Aber nicht an der Stelle. Weniger Keywords vereinfachen die Sprache.
Wenn also Typen keine Keywords sind, wenn auch Attribute wie const,
volatile, static, extern ... so strukturiert werden, dass man nicht für
jedes davon ein Keyword benötigt, dann kann eine Sprache wirklich
einfacher werden. Und leichter erweiterbar.
Lars R. schrieb:> Das ist doch scheußlich!
Bloss anders. Wer auf C geprägt ist, wie die kleine Gans auf die Eltern,
der kann natürlich damit erst einmal ein Problem haben, wenn etwas
auftaucht, das anders aussieht. Ich hatte aber nicht mit C angefangen,
kannte davor schon Sprachen ohne syntaktische Gemeinsamkeit mit C.
Rolf M. schrieb:> Gibt es einen anderen Grund als Tippfaulheit?
Es müssen wohl einige gesehen haben, dass es sich lohnt. Was man schon
daran erkennt, dass es schon vor der Veröffentlichung der ersten
K&R-Auflage nachträglich eingebaut wurde.
Verwendet man den gleichen Datentyp mit der gleichen Bedeutung an
verschiedenen Stellen (z.B. "Helligkeit"), ist ein Typedef dafür sehr
sinnvoll. Baut man komplexere Typen aus Arrays und Pointer auf, wird es
ohne Typedefs schnell unlesbar. Nicht nur, wenn auch Funktionen dabei
sind, wie oben erwähnt.
(prx) A. K. schrieb:> Rolf M. schrieb:>> Gibt es einen anderen Grund als Tippfaulheit?>> Es müssen wohl einige gesehen haben, dass es sich lohnt. Was man schon> daran erkennt, dass es schon vor der Veröffentlichung der ersten> K&R-Auflage nachträglich eingebaut wurde.
Ich meine nicht typedef allgemein, sondern speziell die, die nur dazu
genutzt werden, um be struct-Typen das "struct" weglassen zu können.
1
typedefstructmy_structmy_struct;
(prx) A. K. schrieb:> Verwendet man den gleichen Datentyp mit der gleichen Bedeutung an> verschiedenen Stellen (z.B. "Helligkeit"), ist ein Typedef dafür sehr> sinnvoll.
Wobei an der Stelle von Nachteil ist, dass typedef eben keinen neuen Typ
definiert, d.h. der Compiler kann nicht überprüfen, ob man dort, wo eine
Helligkeit erwartet wird, auch tatsächlich eine solche angegeben hat
oder vielleicht doch stattdessen eine Lautstärke.
Rolf M. schrieb:> Wobei an der Stelle von Nachteil ist, dass typedef eben keinen neuen Typ> definiert
Ja. Ist aber ein völlig anderes Thema. Ada hat deshalb beides -
Typableitung mit und ohne Verträglichkeit des abgeleiten Typs mit dem
Original.
Rolf M. schrieb:> Ich meine nicht typedef allgemein, sondern speziell die, die nur dazu> genutzt werden, um be struct-Typen das "struct" weglassen zu können.
Das ergibt sich als Nebeneffekt eines allgemeinen Typedefs von alleine.
Hab ich mich so ungeschickt ausgedrückt? Ich meine nicht die Existenz
des Typedefs, sondern speziell seine Nutzung für den oben beschriebenen
Anwendungsfall. Ich hatte mich dabei auf diese Aussage bezogen:
Lars R. schrieb:> Aber bei einem struct oder enum> schreibe ich lieber "struct x" bzw. "enum y"...
Also welchen Voteil, außer dass ich bei der Verwendung an ein paar
Stellen das Wörtchen "struct" nicht tippen muss, hat es, wenn ich als
Programmier schreibe:
Der Vorteil ist die flüssigere Lesbarkeit und das weniger an Tipparbeit,
zumindest bei so fundamentalen Typen wie Vector_3d, die 100'erte Male
vorkommen. C++ macht es ja nicht ohne Grund genauso. Da musst du auch
nicht class Vector_3d schreiben. Zugegeben, man kann auch ohne dieses
Feature gut programmieren.
Und das ein typedef nur ein Alias ist, ist aus C Sicht heraus sinnvoll.
Bei fundamentalen integer typen gilt ja automatische Typumwandlung, da
macht
es keinen Sinn für "typedef int my_int_t" eine verwirrende Ausnahme zu
machen.
Will man einen echten eigenen Typ haben, dann macht man einfach ein
"typedef struct { int i; } my_int_t;", und fertig. typedef ist ein
nicht optimal gewählter Name, besser wäre typealias oder typename.
Rolf M. schrieb:> (prx) A. K. schrieb:>> Und sonst nur "struct s" und "union u"?>> Mache ich in C auch so.
Ich auch.
> Warum soll ich hinter einem typedef verstecken, dass es eine struct> ist?
Diese Mode kam IMHO erst mit der Einführung von C++ so richtig auf, wo
man das "struct", "union" und "enum" bei Typnamen generell weglassen
kann, auch ohne typedef. Manche, die sich daran gewöhnt haben, möchten
das auch in C so haben und typedeffen deswegen diese Schlüsselwörter
weg.
> Gibt es einen anderen Grund als Tippfaulheit?
IMHO nur sehr wenige.
Bei der Definition eines API kann es manchmal sinnvoll sein, bei einem
plattformspezifischen Datentyp, der je nach Plattform ein int oder ein
struct sein kann, diesen Unterschied mittels typedef vor dem Benutzer zu
verbergen. Dieses Ziel kann man allerdings auch dadurch erreichen, dass
man auch das int in ein Dummy-struct verpackt, so dass der Benutzer
unabhängig vom tatsächlichen Typ immer ein struct und damit ebenfalls
keinen Unterschied sieht.
Es gibt andererseits eine Stelle, wo struct/union-Namen statt
typedef-Namen wichtig sind, und auch das hat mit dusseliger Syntax zu
tun. Wenn sie innerhalb davon verwendet werden:
1
typedefstructS
2
{
3
structS*next;
4
...
5
}S;
Stünde der Typname vorne, wie es sich gehört, wäre das kein Thema, weil
dann der Typ innerhalb der Struct-Deklaration schon bekannt ist.
1
typeS:struct{...};
C++ hat das repariert, indem der Struct-Name zum Typedef-Name wurde. In
C hätte man eine heilige Kuh schlachten müssen.
Yalu X. schrieb:> Diese Mode kam IMHO erst mit der Einführung von C++ so richtig auf
Hmm. Ich hatte das von Anfang an so gehalten, lange vor C++, und war
damit nicht alleine.
(prx) A. K. schrieb:> C hätte man eine heilige Kuh schlachten müssen.
Hat man ja getan. Fast von Anfang an. Nennt sich Vorwärtsdeklaration und
bezieht sich tatsächlich auf den (sonst irgendwie völlig nutzlosen)
Namen der typedef-Strukturdeklaration.
typedef foo * fooptr_t;
typedef struct foo {
//whatever else
fooptr_t PointerToAnyInstanceOfStructFoo;
}foo_t;
C ist Dreck, völliger struktureller Dreck vom tiefsten Grund der
Sprachdefinition her. Das muss man einfach immer berücksichtigen, dann
fällt es viel leichter, den ganzen stupiden Wahnsinn mit einem
gelassenen Grunzen zu akzeptieren.
c-hater schrieb:> C ist Dreck,
Diese Aussage wiederholst du (sinngemäß) im Mittel einmal pro Woche, und
das schon seit Jahren. Dabei ist diese Information bereits vollständig
in deinem Nick enthalten und deswegen jeder deiner Texte redundant.
Ist das eigentlich dein kompletter Wissensstand, was Computer und
Programmierung betrifft, oder weißt du auch noch etwas anderes?
Yalu X. schrieb:> c-hater schrieb:>> C ist Dreck,>> Diese Aussage wiederholst du (sinngemäß) im Mittel einmal pro Woche, und> das schon seit Jahren. Dabei ist diese Information bereits vollständig> in deinem Nick enthalten und deswegen jeder deiner Texte redundant.>> Ist das eigentlich dein kompletter Wissensstand, was Computer und> Programmierung betrifft, oder weißt du auch noch etwas anderes?
Du weißt sehr genau, dass ich auch noch anderes weiß. Das ist aber
garnicht dein eigentliches Problem, du könntest gut damit umgehen, dass
ich mehr weiß, als dass C als Sprache grundsätzlich ziemlich Scheiße
ist.
Dein Problem ist: dich kotzt es maximal an, dass die von dir präferierte
Sprache recht regelmäßig als eigentlich völlig ziemlich untauglich
entblößt wird. Das geht bei dir an die Substanz, denn du kannst ja nicht
wirklich etwas anderes...
Das vermittelt dir ein gewisses Minderwertigkeitsgefühl. Tja schlecht
für dich. Aber Fakten können auf Befindlichkeiten selbst von Moderatoren
mit Lösch-Macht leider keine Rücksicht nehmen...
c-hater schrieb:> Dein Problem ist: dich kotzt es maximal an, dass die von dir präferierte> Sprache recht regelmäßig als eigentlich völlig ziemlich untauglich> entblößt wird.
Häh? Hier geht es doch um C, wieso fängst du jetzt plötzlich mit Python
und Haskell an (von denen du vermutlich genauso wenig Ahnung wie von C
hast)?
c-hater schrieb:> Du weißt sehr genau, dass ich auch noch anderes weiß. Das ist aber> garnicht dein eigentliches Problem, du könntest gut damit umgehen, dass> ich mehr weiß, als dass C als Sprache grundsätzlich ziemlich Scheiße> ist.
Ich kann dir sagen, was mein Problem damit ist: Dass du offenbar
regelrecht besessen von der Sprache bist. Anders kann ich mir nicht
erklären, warum du trotz dieser Meinung den anscheinend
unwiderstehlichen Drang verspürst, zu jedem, aber auch absolut jedem
Thread über das Theman deinen Senf dazugeben zu müssen.
Es gibt auch Sprachen, die ich nicht mag. Dann ziehe ich aber nicht los
und streue in jede einzelne Diskussion über diese Sprache ein, dass die
doch totaler Mist sei. Sondern ich halte dazu einfach die Klappe.
(prx) A. K. schrieb:> C++ hat das repariert, indem der Struct-Name zum Typedef-Name wurde.
Naja, C++ hat da nichts repariert. Sondern es ist in C++ eine absolute
Notwendigkeit.
Das liegt daran, das man in C im engeren Sinn keine UDT definieren kann.
C structs sind zwar "struktierierte Datentypen" im Sinne der Sprache C,
aber sie sind eben keine (abstrakte) Datentypen im Sinne der Informatik.
Zwar kann man Operationen auf Datentypen in C mit Funktionen realisieren
(das geht für die strukturiereten DT wie für die primitiven DT),
andererseits kann man die Operatoren (+, -, ...) der Sprache zwar auf
die primitiven DT anwenden, aber eben nicht auf die strukturierten
Datentypen.
Dies steht aber im Widerspruch zur generischen Programmierung, was ja
C++ in Form von Templates unterstützt. Hier ist es eben absolut
notwendig, dass UDT und primitive DT gleich mächtig sind, will heißen,
dass man Operationen in Form von Funktionen wie auch in Form von
Operatoren auf beides anwenden kann (hiermit ist nicht Uniform function
call syntax gemeint, sondern es geht um die Idempotenz von UDT und
primitiven DT).
Wilhelm M. schrieb:> (prx) A. K. schrieb:>> C++ hat das repariert, indem der Struct-Name zum Typedef-Name wurde.>> Naja, C++ hat da nichts repariert. Sondern es ist in C++ eine absolute> Notwendigkeit.
Das verstehe ich nicht. Es geht doch um das Schlüsselwort "struct", was
in C dem Typnamen einer Struktur ohne Alias vorangestellt werden muss
und in C++ weggelassen werden kann?
Falls ja: Welche Dinge wären in C++ nicht möglich, wenn dort wie in C
das "struct" verpflichtend wäre?
Yalu X. schrieb:> Falls ja: Welche Dinge wären in C++ nicht möglich, wenn dort wie in C> das "struct" verpflichtend wäre?
Die Frage ist, wo es genau stehen müsste.
Mombert H. schrieb:> Yalu X. schrieb:>> Falls ja: Welche Dinge wären in C++ nicht möglich, wenn dort wie in C>> das "struct" verpflichtend wäre?> Die Frage ist, wo es genau stehen müsste.
Vor dem Namen des Typs, wie in C auch. Ich verstehe auch nicht ganz, was
das mit Operator-Überladung zu tun haben soll.
Rolf M. schrieb:> Ich verstehe auch nicht ganz, was das mit Operator-Überladung zu tun> haben soll.
Ich auch nicht und auch generell nicht, wie das Ganze mit abstrakten
Datentypen zusammenhängt. Ist ein Datentyp denn weniger abstrakt, wenn
man ein "struct" davor schreibt?
Ich sehe höchstens einen ästhetischen Vorteil, da ohne das "struct"
Struktur- und Klassenamen gleich aussehen wie die Namen primitiver
Datentypen.
Aber sicher wird uns Wilhelm gleich erleuchten :)
(prx) A. K. schrieb:> Yalu X. schrieb:>> Ist ein Datentyp denn weniger abstrakt, wenn>> man ein "struct" davor schreibt?>> Schon etwas.> ...>> ... A(1) ...> ... B(1) ...
So ist es natürlich schöner anzusehen, das ist aber auch alles.
Laut Wilhelm scheint da mehr dahinterzustecken:
Wilhelm M. schrieb:> Sondern es ist in C++ eine absolute Notwendigkeit.
Ich habe nur keine Idee, was das sein könnte, deswegen meine Frage oben.
Yalu X. schrieb:> So ist es natürlich schöner anzusehen, das ist aber auch alles.
Das Stichwort hattest du selbst genannt: Abstraktion. Das war ein
wesentliches Ziel von C++. Es sollte kein "C mit Klassen" sein, sondern
bei der Nutzung eines Objektes im Code von der konkreten Implementierung
eines Objektes wegführen. Mit struct vorneweg passt das nicht.
(prx) A. K. schrieb:> Mit struct vorneweg passt das nicht.
"struct" und "class" sind sowieso praktisch identisch in C++. Es gibt
eine einzige Feinheit: "struct" fängt gleich mit "public:" an, "class"
hingegen mit "private:". "struct xy { private: T ..." ist also völlig
gleichbedeutend zu "class xy { T ..."
Rolf M. schrieb:> Mombert H. schrieb:>> Yalu X. schrieb:>>> Falls ja: Welche Dinge wären in C++ nicht möglich, wenn dort wie in C>>> das "struct" verpflichtend wäre?>> Die Frage ist, wo es genau stehen müsste.>> Vor dem Namen des Typs, wie in C auch. Ich verstehe auch nicht ganz, was> das mit Operator-Überladung zu tun haben soll.
1
template<typenameT>
2
autoadd(Ta,Tb)->T{
3
returna+b;
4
};
5
6
structS{
7
intv;
8
};
9
autooperator+(Sa,Sb)->S{
10
returnS{a.v+b.v};
11
}
12
13
voidfoo(){
14
inta=1;
15
intb=42;
16
intc=add(a,b);
17
18
Ssa={1};
19
Ssb={42};
20
Ssc=add(sa,sb);
21
}
Wo soll jetzt überall nen struct stehen müssen/dürfen?
Mombert H. schrieb:> Wo soll jetzt überall nen struct stehen müssen/dürfen?
Na eben wie in C, immer vor dem Namen der Struct, also im Beispiel vor
jedem S. So funktioniert es ja in C. Der Typ heißt dort eben nicht "S",
sondern "struct S", also:
1
template<typenameT>
2
autoadd(Ta,Tb)->T{
3
returna+b;
4
};
5
6
structS{
7
intv;
8
};
9
10
autooperator+(structSa,structSb)->structS{
11
returnstructS{a.v+b.v};
12
}
13
14
voidfoo(){
15
inta=1;
16
intb=42;
17
intc=add(a,b);
18
structSsa={1};
19
structSsb={42};
20
structSsc=add(sa,sb);
21
}
Ich sehe da jetzt kein Problem mit Operatoren, Funktionen oder
Templates.
Wilhelm M. schrieb:> (prx) A. K. schrieb:>> C++ hat das repariert, indem der Struct-Name zum Typedef-Name wurde.
Der Begriff "typedef-Name" in diesem Satz ist nicht definiert, es ist
wohl
der typ-alias gemeint.
> Naja, C++ hat da nichts repariert. Sondern es ist in C++ eine absolute> Notwendigkeit.> Das liegt daran, das man in C im engeren Sinn keine UDT definieren kann.> C structs sind zwar "struktierierte Datentypen" im Sinne der Sprache C,> aber sie sind eben keine (abstrakte) Datentypen im Sinne der Informatik.> Zwar kann man Operationen auf Datentypen in C mit Funktionen realisieren> (das geht für die strukturiereten DT wie für die primitiven DT),> andererseits kann man die Operatoren (+, -, ...) der Sprache zwar auf> die primitiven DT anwenden, aber eben nicht auf die strukturierten> Datentypen.
In der Informatik bestehen Datentypen aus Werten und anwendbaren
Operatoren.
In C haben wir eine Unsymmetrie zwischen strukturierten Datentypen und
primitiven DT, weil man die eingebauten Operatoren nicht für die
strukturierten Datentypen definieren kann. Ergo: strukturierte DT und
primitive
DT sind nicht gleich mächtig.
> Dies steht aber im Widerspruch zur generischen Programmierung, was ja> C++ in Form von Templates unterstützt. Hier ist es eben absolut> notwendig, dass UDT und primitive DT gleich mächtig sind, will heißen,> dass man Operationen in Form von Funktionen wie auch in Form von> Operatoren auf beides anwenden kann (hiermit ist nicht Uniform function> call syntax gemeint, sondern es geht um die Idempotenz von UDT und> primitiven DT).
Bei der generischen Programmierung ist das Überladen von
Operatorfunktionen
(das Definieren von Operationen) für UDT kein syntaktischer Zucker mehr,
sondern notwendig:
1
automean(autoa,autob){
2
return(a+b)/2;
3
}
Das obige Beispiel funktioniert nur für beliebige (UDT und prim. DT),
wenn
man die eingebauten Operatoren für UDT definieren
(überladene Operatorfunktionen) kann.
In C++ ist der Name des struct / class der Datentyp-Name.
1
template<typenameT>
2
automean(Ta,Tb){
3
return(a+b)/T{2};
4
}
T wird also hier zum Namen des struct / class (Typnamen) abgeleitet.
Müsste man nach wie vor ein Schlüsselwort struct davor schreiben, so
würde das Beispiel ja nicht mehr funktionieren, weil es
1
template<typenameT>
2
automean(Ta,Tb){
3
return(a+b)/structT{2};
4
}
heißen müsste.
Dies wäre eine weitere Unsymmetrie zwischen UDT und prim. DT, die die
generische Programmierung verhindern würde.
DerEgon schrieb:> Ein typedef verhält sich aber wie ein Typ, insofern ist die> Unterscheidung ziemlich irrelevant.
Einen "typedef" gibt es nicht. Stattdessen gibt es eine in C eingeführte
Anweisung
typedef alterName neuerName;
und das setzt eigentlich voraus, daß es für eine typedef-Anweisung einen
bereits zuvor definierten Typ geben müßte. Bloß bei struct's hat man
eine Ausnahme gemacht, dahingehend, daß besagter Typ (in diesem Fall ein
struct) auch innerhalb der typedef-Anweisung hingeschrieben werden kann.
Eben so, wie du es bereits geschrieben hast:
1
typedefstruct
2
{intx;
3
inty;
4
intz;
5
}blafusel;
Wie man sieht, ist der eigentlich definierte struct namenlos, weswegen
er im Programm auch nicht benutzt werden kann. Aber er kann über den mit
typedef zugewiesenen Zweitnamen "blafusel" benutzt werden.
Also: die Bezeichnung "typedef" ist eigentlich irreführend. Es ist
realiter nur ein "create_alias".
W.S.
Wilhelm M. schrieb:> T wird also hier zum Namen des struct / class (Typnamen) abgeleitet.>> Müsste man nach wie vor ein Schlüsselwort struct davor schreiben, so> würde das Beispiel ja nicht mehr funktionieren, weil es> template<typename T>> auto mean(T a, T b) {> return (a + b) / struct T{2};> }>> heißen müsste.
Nein, da 'struct' bereits Teil des Namens des Typs ist. Das kann man an
der Stelle nicht noch ein zweites mal angeben.
Wenn T z.B. ein struct X ist, dann macht es keinen Sinn, zusätzlich noch
"struct T" draus zu machen. Wie beim Typedef:
1
typedefstructXT;
2
structTt;// Fehler
W.S. schrieb:> Einen "typedef" gibt es nicht. Stattdessen gibt es eine in C eingeführte> Anweisung> typedef alterName neuerName;> und das setzt eigentlich voraus, daß es für eine typedef-Anweisung einen> bereits zuvor definierten Typ geben müßte.
Es muss ein an dieser Stelle namentlich bekannter Typ sein. Ob der zuvor
oder genau dort definiert wurde, ist egal. Er muss an der Stelle noch
nicht mal überhaupt definiert sein. Es reicht schon aus, wenn er dort
nur implizit deklariert wurde:
1
typedefstructXY;
2
3
structX{inti;};
4
5
intmain()
6
{
7
Yfoo;
8
foo.i=3;
9
}
> Bloß bei struct's hat man eine Ausnahme gemacht, dahingehend, daß> besagter Typ (in diesem Fall ein struct) auch innerhalb der typedef> -Anweisung hingeschrieben werden kann.
Das ist keine Ausnahme, sondern ergibt sich automatisch, wenn man es
nicht explizit verbietet.
> Also: die Bezeichnung "typedef" ist eigentlich irreführend. Es ist> realiter nur ein "create_alias".
Richtig.
Wilhelm M. schrieb:> Müsste man nach wie vor ein Schlüsselwort struct davor schreiben, so> würde das Beispiel ja nicht mehr funktionieren
Nein, weil das T ja entweder "struct S" oder "int" heißt. Es müsste vor
dem Template-Parameter nicht nochmals "struct" stehen.
Ich sehe daher auch keinen Grund, wieso dies die genetische
Programmierung verhindern sollte...
(prx) A. K. schrieb:> Zum C++ Compiler für Gene ists hoffentlich noch eine Weile hin. ;-)
Habe auf dem Smartphone getippt. Generisch korrigiert er mir ungewollt.
Ich bitte um Verzeihung, hätte besser vor dem Absenden schauen müssen!
Rolf M. schrieb:> Wilhelm M. schrieb:>> T wird also hier zum Namen des struct / class (Typnamen) abgeleitet.>>>> Müsste man nach wie vor ein Schlüsselwort struct davor schreiben, so>> würde das Beispiel ja nicht mehr funktionieren, weil es>> template<typename T>>> auto mean(T a, T b) {>> return (a + b) / struct T{2};>> }>>>> heißen müsste.>> Nein, da 'struct' bereits Teil des Namens des Typs ist. Das kann man an> der Stelle nicht noch ein zweites mal angeben.Lars R. schrieb:> Nein, weil das T ja entweder "struct S" oder "int" heißt. Es müsste vor> dem Template-Parameter nicht nochmals "struct" stehen.>> Ich sehe daher auch keinen Grund, wieso dies die genetische> Programmierung verhindern sollte...
Ja, genau das ist der Punkt, warum ich oben ein Verständnisproblem
hatte, das sich jetzt aber in Wohlgefallen aufgelöst hat:
Yalu X. schrieb:> Wilhelm M. schrieb:>> (prx) A. K. schrieb:>>> C++ hat das repariert, indem der Struct-Name zum Typedef-Name wurde.>>>> Naja, C++ hat da nichts repariert. Sondern es ist in C++ eine absolute>> Notwendigkeit.>> Das verstehe ich nicht.
Wie es aussieht, kann man also auch in C++ vor alle Strukturnamen, die
nicht schon ein Alias sind (und damit das "struct" implizit enthalten),
problemlos ein "struct" davorsetzen, muss es im Gegensatz zu C aber
nicht.
Lässt man das "struct" weg, macht man damit deutlich, dass es sich
sowohl bei Strukturen als auch bei primitiven Typen um abstrakte
Datentypen (also Einheiten aus Daten und zugehörigen Operationen), und
damit um etwas logisch Ähnliches handelt.
Man verwischt damit allerdings auch die Unterschiede, die durchaus
bestehen:
- Von einer Struktur oder Klasse kann man einen neuen Typ ableiten, von
einem primitiven Typ nicht.
- Auf die Member-Funktionen einer Struktur oder Klasse kann man per
"."-Operator zugreifen, bei primitiven Datentypen geht das nicht.
Auf Grund dieser Unterschiede wäre IMHO auch nicht völlig verkehrt, das
(optionle) "struct" hinzuschreiben, wenngleich ich niemanden kenne, der
das tatsächlich tut.
Yalu X. schrieb:> Wie es aussieht, kann man also auch in C++ vor alle Strukturnamen, die> nicht schon ein Alias sind (und damit das "struct" implizit enthalten),> problemlos ein "struct" davorsetzen, muss es im Gegensatz zu C aber> nicht.
Nein! Z.B. das struct nach dem return ist nicht erlaubt:
Rolf M. schrieb:> auto operator+(struct S a, struct S b) -> struct S {> return struct S{a.v + b.v};> }Yalu X. schrieb:> Auf Grund dieser Unterschiede wäre IMHO auch nicht völlig verkehrt, das> (optionle) "struct" hinzuschreiben, wenngleich ich niemanden kenne, der> das tatsächlich tut.
Ich glaube nicht, dass C++ dadurch lesbarer wird. Warum ist es außerhalb
von templates so wichtig, dass man ein struct dran schreibt, in einem
template aber nicht mehr? Man kann die spassigen Fälle haben, dass man
ein template mit Argument "struct s" instanziert, dass in der Definition
des templates aber ein "class T" Parameter ist, oder so aussieht. Und
man kann generell nicht das gleiche mit Klassen machen, obwohl sie ja
"nur struct mit 'private:' sind". ...
Mombert H. schrieb:> Yalu X. schrieb:>> Wie es aussieht, kann man also auch in C++ vor alle Strukturnamen, die>> nicht schon ein Alias sind (und damit das "struct" implizit enthalten),>> problemlos ein "struct" davorsetzen, muss es im Gegensatz zu C aber>> nicht.> Nein! Z.B. das struct nach dem return ist nicht erlaubt:> Rolf M. schrieb:>> auto operator+(struct S a, struct S b) -> struct S {>> return struct S{a.v + b.v};>> }
Das S in der return-Anweisung ist – anders als im Funktionskopf in der
Zeile darüber – nicht der Typname, sonder der Name des Konstruktors,
deswegen ist hier das "struct" fehl am Platz.
Mombert H. schrieb:> Yalu X. schrieb:>> Auf Grund dieser Unterschiede wäre IMHO auch nicht völlig verkehrt, das>> (optionle) "struct" hinzuschreiben, wenngleich ich niemanden kenne, der>> das tatsächlich tut.>> Ich glaube nicht, dass C++ dadurch lesbarer wird. Warum ist es außerhalb> von templates so wichtig, dass man ein struct dran schreibt, in einem> template aber nicht mehr?
Es ist überhaupt nicht wichtig, deswegen tut das ja auch niemand (mich
eingeschlossen). Andererseits sind für mich Wilhelms Argumente für das
Weglassen des "struct" bzw. für die "absolute Notwendigkeit", dies zu
tun, aus den in meinem letzten Beitrag genannten Gründen auch nicht
hundertprozentig stichhaltig.
Da man sich aber letztendlich für eine der beiden Alternativen (mit oder
ohne "struct") entscheiden muss und logische Überlegungen dabei nur
begrenzt weiterhelfen, geht man am besten ganz pragmatisch vor und wählt
die Option mit der wenigsten Tipparbeit, die auch gleichzeitig die in
den C++Lehrbüchern propagierte ist :)
Yalu X. schrieb:> Das S in der return-Anweisung ist – anders als im Funktionskopf in der> Zeile darüber – nicht der Typname, sonder der Name des Konstruktors,> deswegen ist hier das "struct" fehl am Platz.
Bist du dir sicher, dass da der Name des Konstruktors steht? Welcher
Konstruktor wird aufgerufen? Was passiert, wenn S einen S(int)
Konstruktor hätte und die Funktion so aussieht:
1
autooperator+(Sa,Sb)->S{
2
returnS(a.v+b.v);
3
}
Was passiert, wenn die Funktion einen int zurück liefert?
1
autooperator+(Sa,Sb)->int{
2
returnint(a.v+b.v);
3
}
Welcher Konstruktor wird hier aufgerufen?
Yalu X. schrieb:> Es ist überhaupt nicht wichtig, deswegen tut das ja auch niemand (mich> eingeschlossen).
Seid wann muss etwas wichtig sein, damit wir hier über Vor- und
Nachteile diskutieren? ;-) Auch wenn es nicht wichtig ist, ich habe
schonmal etwas gelernt (Ich bin bis jetzt davon ausgegangen, dass hinter
dem return ein expliziter Aufruf eines Konstruktors steht und hatte bis
jetzt noch keinen Grund diese Annahme zu überdenken. Vielen Dank für
diese Anregung! :) )
Yalu X. schrieb:> Das S in der return-Anweisung ist – anders als im Funktionskopf in der> Zeile darüber – nicht der Typname, sonder der Name des Konstruktors,> deswegen ist hier das "struct" fehl am Platz.
Das ist hier nicht der Grund. An dieser Stelle wird ja ein Ausdruck
erwartet, jedoch wird `struct S` nicht als Typ geparsed, hier müssen wir
dem Compiler wieder etwas helfen:
1
return(structS){42};
Also die Assoziativität der Tokens verändern. Dann geht es wieder.
Mombert H. schrieb:> Bist du dir sicher, dass da der Name des Konstruktors steht? Welcher> Konstruktor wird aufgerufen? Was passiert, wenn S einen S(int)> Konstruktor hätte und die Funktion so aussieht:auto operator+(S a, S b)> -> S {> return S(a.v + b.v);> }> Was passiert, wenn die Funktion einen int zurück liefert?auto> operator+(S a, S b) -> int {> return int(a.v + b.v);> }> Welcher Konstruktor wird hier aufgerufen?
Es wird `S(int)`, d.h. der Typwandlungs-ctor bzw. int(int), d.h. der
c-ctor aufgerufen.
Mombert H. schrieb:> (Ich bin bis jetzt davon ausgegangen, dass hinter> dem return ein expliziter Aufruf eines Konstruktors steht und hatte bis> jetzt noch keinen Grund diese Annahme zu überdenken.
Nein, hinter return muss ein Ausdruck stehen.
Eine weitere Inkonsistenz ist, dass der Template-Typ-Parameter zu `S`
abgeleitet wird, selbst wenn das template mit `struct S` parametriert
wird. Insgesamt sieht man daran, dass diese Möglichkeit, den Typnamen
mit dem struct-Namen (das ist nicht der Typ) gleichzusetzen, bei C++ aus
Kompatibilitätsgründen zu C da ist.
Es gibt allerdings auch den umgekehrten Fall in C++, wo das
Schlüsselwort `struct` als disambiguation keyword notwendig ist (hier
dann also nicht aus Kompatibilitätsgründen zu C, sondern für C++ selbst
notwendig):
1
structS1{
2
S1(intx=0):x{x}{}
3
intx{};
4
};
5
6
voidS1(){
7
}
8
9
template<typenameT>
10
autofoo(Ta){
11
// T::_;
12
return(structS1){a};// Assoziativität (notwendig bei Ausdrücken)
13
}
14
15
intmain(){
16
foo((structS1){});// Assoziativität (notwendig bei Ausdrücken)
17
18
structS1s1;// Disambiguation: S1 s1; funktioniert nicht mehr, wenn Funktion S1() deklariert ist
Wilhelm M. schrieb:> Es gibt allerdings auch den umgekehrten Fall
Ein schönes Beispiel dafür, daß man nicht alles, was geht, auch
tatsächlich tun sollte.
Oliver
Oliver S. schrieb:> Wilhelm M. schrieb:>> Es gibt allerdings auch den umgekehrten Fall>> Ein schönes Beispiel dafür, daß man nicht alles, was geht, auch> tatsächlich tun sollte.
Genau, auch wenn es hier gar nicht darum ging, sondern nur um
Möglichkeiten.
Und dies ist auch eher ein C als ein C++ Thema, denn in C gibt es keine
Namensräume wie in C++, mit denen man solche Namenskollisionen (zusammen
mit ADL) entschärfen könnte.