Hallo Leute,
ich möchte gerne für eine C++ member funktion den return Wert
automatisch bestimmen.
Konkret habe ich eine template Klasse, die einen Punkt definiert. (Diese
hier, nur ein wenig erweitert:
http://stackoverflow.com/questions/11891192/c-templates-simple-point-class?answertab=votes#tab-top)
Ich würde gerne zwei Punkte mit unterschiedlichen typen addieren (z.B.
float und int), wobei der cast automatisch ausgeführt werden soll (wie
beim normalen addieren.
Ich habe das mal so runtergeschrieben:
1
template<typename newTypeT>
2
friend Point<newTypeT, dimensionsCount> operator+(Point const& a, Point const& b)
3
{
4
auto tmp = a[0] * b[0];
5
6
Point<decltype(tmp), dimensionsCount> ret();
7
for (int i = 0; i < dimensionsCount; ++i) {
8
ret[i] = a[i] * b[i];
9
}
10
11
return ret;
12
}
Leider funktioniert das so nicht richtig.
Hat jemand einen Tipp, wie das richtig gelöst werden kann?
Template Neuling schrieb:> Leider funktioniert das so nicht richtig.
Definiere "funktioniert so nicht richtig".
Nebenbei erwähnt ist das hier:
Point<decltype(tmp), dimensionsCount> ret();
die Deklaration einer Funktion, die keine Parameter nimmt und einen
Point<...> zurückgibt. So geht's besser:
Point<decltype(tmp), dimensionsCount> ret{};
Wenn ich dich richtig verstehe, willst du quasi einen Operator der etwa
so wie f in diesem Beispiel reagiert:
1
intf();
2
floatf();
3
intx=f();
4
floaty=f();
Soetwas ist nicht möglich, egal ob Operator oder Funktion, egal ob mit
oder ohne Templares. Die rückgabewerte sind meineswissens nicht teil der
Funktionssignatur, der Compiler kann sie nicht unterscheiden und kann
nicht die richtige wählen.
Was jedoch möglich sein sollte ist sowas: (ungetestet)
Hierbei ist der rückgabewert von den Typen der Argumente abhängig,
wodurch die Funktionssignatur immer eindeutig ist.
tictactoe schrieb:> Nebenbei erwähnt ist das hier:>> Point<decltype(tmp), dimensionsCount> ret();>> die Deklaration einer Funktion
In C++ ist das auch eine valide Definition samt Initialisierung. Das
sollte schon stimmen, mochte ich auch nie.
Daniel A. schrieb:> Wenn ich dich richtig verstehe, willst du quasi einen Operator der etwa> so wie f in diesem Beispiel reagiert:>
1
>intf();
2
>floatf();
3
>intx=f();
4
>floaty=f();
5
>
> Soetwas ist nicht möglich, egal ob Operator oder Funktion, egal ob mit> oder ohne Templares. Die rückgabewerte sind meineswissens nicht teil der> Funktionssignatur, der Compiler kann sie nicht unterscheiden und kann> nicht die richtige wählen.
Warum sollte es nicht?
1
template <class Type> auto f1(Type x) {
2
return x + (Type)1;
3
}
4
5
struct TestStruct {
6
double getVal() { return 4.4; }
7
};
8
9
template <class Type> auto operator+(TestStruct a, Type b) {
@Arc Net (arc)
Betrachte mein 1. Beispiel und deins nochmal ganz genau. Bei meiner
Aussage & Beispiel geht es um Funktionen oder Operatoren mit identischem
Namen und Argumenten aber unterschiedlichem Return type im selben Scope.
Meine Aussage bezog sich explizit auf mein Beispiel. Dein Beispiel zeigt
eine ganz andere Ausganslage. Ausserdem, was war an der Erklärung
unklar?
Daniel A. schrieb:
9573:
>> Nebenbei erwähnt ist das hier:>>>> Point<decltype(tmp), dimensionsCount> ret();>>>> die Deklaration einer Funktion>> In C++ ist das auch eine valide Definition samt Initialisierung. Das> sollte schon stimmen, mochte ich auch nie.
Bist du dir da sicher?
In C++ gibt es eine Mehrdeutigkeit
1
objfoo();
das könnte sein
* der Funktionsprotoyp einer Funktion foo, die keine Argumente nimmt und
ein obj Objekt zurückliefert
* oder die Definition einer Variablen foo, die vom Typ obj ist und die
zur Initialisierung mit dem Default-Constructor vorgesehen ist.
Meines Wissens wird hier so vorgegangen:
If it looks like a function declaration, then it is one.
1
classobj
2
{
3
public:
4
obj(){}
5
6
inta;
7
};
8
9
intmain()
10
{
11
objfoo();
12
13
foo.a=5;// error C2228: left of '.a' must have class/struct/union
14
15
return0;
16
}
Ich hab mir angewöhnt, bei Objekten für die ich den Default Konstruktor
haben möchte, ganz einfach keine Initialisierungsklammern zu schreiben.
Den subtilen Unterschied mit der Zero-Initialisierung ignoriere ich bzw.
den nutze ich nicht aus.
Ich habe Dein Beispiel mal verkürzt, der Return-Wert lässt sich in C++11
über ein decltype relativ bequem ermitteln (in C++14 könntest Du ihn weg
lassen; das wäre dann das Beispiel von Arc Net):
1
template < typename T >
2
struct Point {
3
T operator[](int) const;
4
};
5
6
template < typename A, typename B >
7
auto operator+(const Point<A>& a, const Point<B>& b ) -> Point< decltype( A() * B() ) >
Karl H. schrieb:> Ich hab mir angewöhnt, bei Objekten für die ich den Default Konstruktor> haben möchte, ganz einfach keine Initialisierungsklammern zu schreiben.> Den subtilen Unterschied mit der Zero-Initialisierung ignoriere ich bzw.> den nutze ich nicht aus.> obj foo;> dann ist alles eindeutig.
Oben wurde schon die seit C++11 (zumindest laut Stroustrup) zu
bevorzugende Variante genannt:
Daniel A. schrieb:> In C++ ist das auch eine valide Definition samt Initialisierung. Das> sollte schon stimmen, mochte ich auch nie.
Nope. Das ist eine Funktionsdeklaration.
Oder um es mit den Worten des Compilers zu sagen:
1
objvsfunc.cpp:5:9: warning: empty parentheses interpreted as a function declaration [-Wvexing-parse]
2
obj foo();
3
^~
4
objvsfunc.cpp:5:9: note: remove parentheses to declare a variable
Erstmal vielen Dank für die vielen Antworten!
Ich habe das mal versucht für meinen Fall anzupassen (ich habe noch die
Anzahl der Dimensionen, die der Punkt speichert als Template parameter.
(Es macht in meinen Anwendungen nur Sinn Punkte gleicher
Dimensionsanzahl mit einander zu addieren.) )
1
template<typename A, typename B>
2
auto operator+(const Point<A, dimensionsCount>& a, const Point<B, dimensionsCount>& b) -> Point< decltype( A() + B() ) , dimensionsCount>
Allerdings wird hier dieser Fehler angezeigt:
" error: 'Point<decltype ((A() + B())), dimensionsCount> Point<T,
dimensionsCount>::operator+(const Point<A, dimensionsCount>&, const
Point<B, dimensionsCount>&)' must take either zero or one argument"
Ich kann mit dem Fehler nichts anfangen, denn der '+' Operator sollte
doch zwei Argumente erwarten.
Wo liegt hier das Problem?
Ich habe die Ponit.h mal angehängt, falls jemand reinschauen möchte.
Mit auto return type deduction hat das überhaupt nichts zu tun. Dazu
müsste man schon 'friend auto …' oder 'friend decltype(auto) …' (C++14)
schreiben. Würde dir aber auch nichts nützen. Denn den Typ der Variable
die du zurückgeben willst brauchst du bei deren Deklaration sowieso. Man
spart sich den Typ halt einmal:
Damit umgehst du auch gleich das Problem Punkte mit unterschiedlicher
Dimensionalität zu verknüpfen. Den operator[] zu überladen um den in
decltype zu klatschen halte ich für höchst fragwürdig. common_type wurde
für dieses Szenario gemacht und sollte hier verwendet werden.
In C++14 kann man dann auch std::common_type_t<...> benutzen und spart
sich ein 'typename' und ein '::type'.
Template Neuling schrieb:> Ich kann mit dem Fehler nichts anfangen, denn der '+' Operator sollte> doch zwei Argumente erwarten.> Wo liegt hier das Problem?
Kein 'friend' und das Ding inline definiert. Entweder du hast einen
Parameter (die rechte Seite des +-Ausdrucks) und 'this' ist die linke
Seite, oder du deklarierst das als 'friend' und musst sowohl linke als
auch rechte Seite als Parameter übergeben.
Das mit common_type zu lösen ist natürlich viel schöner! Danke dafür!
Hannes W. schrieb:> size_t /* ? */ dimCount
Ich hab das mal auf int gesetzt. size_t ist ja eher auf Speicher
bezogen. Bei mir geht es sowieso nur um wenige Dimensionen.
Ich habe die Lösung
1
template<typename LHS, typename RHS, int dimCount, typename Result = typename std::common_type<LHS, RHS>::type>
2
friend auto operator+(Point<LHS, dimCount> const& a, Point<RHS, dimCount> const& b)
3
{
4
Point<Result, dimCount> tmp;
5
/* … */
6
return tmp;
7
}
mal eingepflegt, allerdings schmeißt der Compiler den Fehler "non-static
data member declared 'auto'".
Warum denn 'data member', es ist doch eine Funktion?
Template Neuling schrieb:> Ich habe die Lösung> mal eingepflegt, allerdings schmeißt der Compiler den Fehler "non-static> data member declared 'auto'".
Wenn Du wirklich C++11 meinst, dann must Du den Return-Typen der
Funktion angeben:
1
template<typename LHS, typename RHS, int dimCount >
2
auto operator+(Point<LHS, dimCount> const& a, Point<RHS, dimCount> const& b) -> Point< typename std::common_type<LHS, RHS>::type, dimCount >
Template Neuling schrieb:> Hannes W. schrieb:>> size_t /* ? */ dimCount> Ich hab das mal auf int gesetzt. size_t ist ja eher auf Speicher> bezogen. Bei mir geht es sowieso nur um wenige Dimensionen.
Aha. Und was hat eine Dimension von -2 für eine Semantik? Dann nimm
wenigstens unsigned. Ich persönlich würde immer die Bitbreite des Typs
explizit angeben, z.B. uint32_t, damit funktioniert es auf allen
Plattformen. size_t bietet sich deshalb an, da du ja intern ein
Array/Vector benutzt, dessen Größe (size_type) was ist? size_t.
Torsten R. schrieb:>> mal eingepflegt, allerdings schmeißt der Compiler den Fehler "non-static>> data member declared 'auto'".>> Wenn Du wirklich C++11 meinst, dann must Du den Return-Typen der> Funktion angeben:
Äh ja, sorry. Ich benutzte kein C++11, nur C++14/1z :)
Klaus W. schrieb:> Nur mal am Rand: was soll eigentlich rauskommen, wenn man zwei> Punkte> addiert? Ein Doppelpunkt?
Einen Punkt kann man immer auch als einen Ursprungsvektor auffassen.
Damit wird's eine Vektoraddition. Die Implementation von operator+ im
ersten Post implementiert allerdings das Hadamard-Produkt. Wenn der TO
den Code bei mir abliefern würde …
Hannes W. schrieb:> Einen Punkt kann man immer auch als einen Ursprungsvektor auffassen.
Genau darauf wollte ich raus...
Punkte zu addieren ist Blödsinn; wenn man Vektoren meint, sollte man sie
auch so nennen.
Template Neuling schrieb:> Das mit common_type zu lösen ist natürlich viel schöner! Danke> dafür!
Wobei man noch anmerken sollte, dass man mit common_type den Typ kriegt
in den man beiden Typen konvertieren kann. Für eigene Typen ist das
nicht zwingend der Typ der bei einer Addition/Multiplikation rauskommt.
Für Builtin Types passt es aber.
Klaus W. schrieb:> Punkte zu addieren ist Blödsinn; wenn man Vektoren meint, sollte man sie> auch so nennen.
Ich finde es gut und Praktisch, wenn Punkte addiert, multipliziert, etc.
werden können. Wer dabei lieber an Vektoren denkt kann ja "using"
verweden:
1
// Ungetestet
2
template<typenameT,intdimensionsCount>
3
usingVector=Point<T,dimensionsCount>;
Um mal einige Usecases für Punktaditionen zu bringen:
Mittelpunkt Berechnen:
Daniel A. schrieb:> Wer dabei lieber an Vektoren denkt kann ja "using"> verweden
Ich denk dabei auch immer an Vektoren. Punktaddition habe ich noch nie
gehört (Google spuckt dabei einen haufen Ergebnisse zu elliptischen
Kurven aus).
Sebastian V. schrieb:> Daniel A. schrieb:>> Wer dabei lieber an Vektoren denkt kann ja "using">> verweden>> Ich denk dabei auch immer an Vektoren. Punktaddition habe ich noch nie> gehört.
Geht mir auch so. Und Multiplikation schon gar nicht. Man kann einen
Vektor zu einem Punkt addieren, dann bekommt man einen neuen Punkt, aber
man kann keine zwei Punkte addieren. Das ist ähnlich wie bei
Speicheraddressen. Man kann zu einem Zeiger einen Offset addieren und
bekommt einen neuen Zeiger. Man kann aber keine zwei Zeiger addieren.
Das man Punkte nicht addieren kann mag ja Mathematisch gesehen stimmen,
aber in einem Programm ist es einfach einfacher und Effizienter nicht
extra zwischen Punkten und Vektoren zu unterscheiden. Schaut euch z.B.
GLSL oder HLSL an, dort gibt es nur Vektoren und keine Punkte, und
Punkte werden als Vektoren behandelt. Das ist die selbe Situation um
180° gedreht. Punkt oder Vektor ist nur Gedanklich ein unterschied, im
kartesisches Koordinatensystem haben beide die gleichen Komponenten.
Daniel A. schrieb:> Punkt oder Vektor ist nur Gedanklich ein unterschied
Jein. Bei einem Punkt bezieht man sich ja immer auf den Ursprung und
gibt die Koordinaten relativ zu diesem an. Ein Vektor kann einen
beliebigen Anfangs- und Endpunkt haben.
Dem Punkt fehlt sozusagen ein Freiheitsgrad ;-)
Im Grunde genommen ist das der Code, den Torsten Robitzki am 24.12.2015
08:59 vorgeschlagen hat, nur nicht als friend deklariert (sodass 'this'
die linke Seite ist).
Eigentlich wäre mir die Version mit zwei Operanden lieber, weil ich dann
eine Referenz anstatt des this Pointers verwenden könnte. Wenn ich der
Funktion von Torsten Robitzki aber einfach ein friend davor packe,
kompiliert der Code nicht, und zwar mit der Begründung: redifinition of
'... operator+ ...' und zwar mit 'previously definded' in derselben
Zeile. Wie kommt das?
Klaus W. schrieb:> Nur mal am Rand: was soll eigentlich rauskommen, wenn man zwei Punkte> addiert? Ein Doppelpunkt?
Wie schon andere hier sagten, es ist eine Vektor addition. Ich habe als
Namen Point gewählt, weil mir Vector mit vector aus der STL zu leicht
verwechselbar ist. Für eine sinnvollere Namensgebung bin ich offen!
Allerdings muss ich sagen, dass ich Point auch für Punkte in
mehrdimensionalen Arrays verwende. Da kann man dann sinnvoll eine
Elementenweise größer/kleiner Beziehung einführen, die in einem normalen
Vektor der größer/kleiner Beziehung in der 1-Norm entsprechen würde. Da
diese für Vektoren eher selten benutzt wird, finde ich Point gar nicht
so verkehrt.
Hannes W. schrieb:> Die Implementation von operator+ im> ersten Post implementiert allerdings das Hadamard-Produkt. Wenn der TO> den Code bei mir abliefern würde …
Das war ein copy paste Fehler, sorry.
Sebastian V. schrieb:> Wobei man noch anmerken sollte, dass man mit common_type den Typ kriegt> in den man beiden Typen konvertieren kann. Für eigene Typen ist das> nicht zwingend der Typ der bei einer Addition/Multiplikation rauskommt.> Für Builtin Types passt es aber.
Ich verwende in meiner Anwendung nur built-in types für Point. Die
Anmerkung ist aber natürlich sehr gerechtfertigt! Danke dafür!
Mark B. schrieb:> Daniel A. schrieb:>> Punkt oder Vektor ist nur Gedanklich ein unterschied>> Jein. Bei einem Punkt bezieht man sich ja immer auf den Ursprung und> gibt die Koordinaten relativ zu diesem an. Ein Vektor kann einen> beliebigen Anfangs- und Endpunkt haben.>> Dem Punkt fehlt sozusagen ein Freiheitsgrad ;-)
Dem würde ich nicht zustimmen. Der Vektor hat diesen Freiheitsgrad eben
nicht, denn egal wohin du ihn schiebst, anhand des Vektors selbst kannst
du das nicht unterscheiden. Das is so, als würdest du beim Punkt den
Nullpunkt zusammen mit dem Punkt woanders hinschieben.
Daniel A. schrieb:> Um mal einige Usecases für Punktaditionen zu bringen:>> Mittelpunkt Berechnen
Das ist eine der Anwendungen, die ich dafür geplant habe =)
lösen, weil ich dann eine Referenz anstatt des this Pointers verwenden
könnte (und weil es mich einfach interessiert).
Wenn ich der
Funktion von Torsten Robitzki aber einfach ein friend davor packe,
kompiliert der Code nicht, und zwar mit der Begründung: redifinition of
'... operator+ ...' und zwar mit 'previously definded' in derselben
Zeile. Hat jemand einen Tipp, wie das zustande kommt?
> lösen, weil ich dann eine Referenz anstatt des this Pointers verwenden> könnte (und weil es mich einfach interessiert).
Wo siehst Du den einen this pointer in einer freien Funktion (operator+)
und wo siehst Du hier überhaupt eine Anwendung für `friend` in Deinem
code. Friend deklariert eine Funktion (nicht member), die auf die
privaten member eines Objekts zugreifen darf. Wenn operator[] bei Deiner
Klasse nicht privat ist, gibt es exakt gar keinen Grund, den operator+
zum friend zu deklarieren.
Am einfachsten wird es, wenn Du Dein Beispiel mal weiter eindampfst und
kompilierbaren Code zeigst.
> Funktion von Torsten Robitzki aber einfach ein friend davor packe,> kompiliert der Code nicht, und zwar mit der Begründung: redifinition of> '... operator+ ...' und zwar mit 'previously definded' in derselben> Zeile. Hat jemand einen Tipp, wie das zustande kommt?
das ist typischer Weise eine Fehlermeldung des linkers und bedeutet,
dass der Linker die Definition einer Funktion in zwei
Übersetzungseinheiten hat. Das passiert z.b., wenn man eine nicht
statische Funktion in einem header definiert und den header an mehr als
einer Stelle einbindet. Was das Problem bei Dir ist, kann keiner sagen,
wenn Du nicht ein bisschen mehr von Deinem Code zeigst.
Der Fehler kommt wohl daher, dass beide Klassen Point<int> und
Point<double> die gleiche Definition von operator+ bereit stellen. Damit
gibt es zwei Definitionen. Eine Mögliche Lösung ist in der Klasse die
Funktion nur als friend zu deklarieren und später zu definieren. Oder
was auch zu funktionieren scheint ist wenn man nur einen Template
Parameter nimmt und den anderen durch die Klasse vorgibt:
Edit: Natürlich gilt die Anmerkung von Torsten Robitzki, dass man sich
überhaupt erstmal fragen sollte warum man friend benutzen möchte und
dann noch die etwas wirre Konstruktion mit der Definition in der Klasse.
Hallo und ein frohes neues euch allen!
Torsten R. schrieb:> Wo siehst Du den einen this pointer in einer freien Funktion (operator+)
Das war auf die Lösung vom 27.12.2015 21:32 bezogen. Wenn man den
Operator mit nur einem Argument definiert, dann zeigt der this Pointer
auf das linksseitige Element.
Torsten R. schrieb:> Du hier überhaupt eine Anwendung für `friend` in Deinem> code. Friend deklariert eine Funktion (nicht member), die auf die> privaten member eines Objekts zugreifen darf. Wenn operator[] bei Deiner> Klasse nicht privat ist, gibt es exakt gar keinen Grund, den operator+> zum friend zu deklarieren.
Man kann den operator nicht mit zwei Variablen und als nicht friend
deklarieren, sonst kommt der Fehler "...operator... must take either
zero or one argument"
Sebastian V. schrieb:> Der Fehler kommt wohl daher, dass beide Klassen Point<int> und> Point<double> die gleiche Definition von operator+ bereit stellen.
Vielen Dank für diesen Tipp! Da wäre ich nicht ohne weiteres selbst
drauf gekommen!
Sebastian V. schrieb:> Eine Mögliche Lösung ist in der Klasse die> Funktion nur als friend zu deklarieren und später zu definieren.
Soweit ich weiß, kann man template Klassen nicht in einer separaten .cpp
definieren. Hierzu dieser Link:
http://stackoverflow.com/questions/1724036/splitting-templated-c-classes-into-hpp-cpp-files-is-it-possibleSebastian V. schrieb:> Oder> was auch zu funktionieren scheint ist wenn man nur einen Template> Parameter nimmt und den anderen durch die Klasse vorgibt
Jop, das funktioniert und ist bei mir gar nicht so abwegig, da meine
Klasse diesen template Parameter sowieso definiert!
Rolf M. schrieb:> In den meisten Fällen ist es einfacher, die eigentliche Addition in> einen Operator += zu stecken und den Operator + dann nur noch darauf> aufzusetzen.
Das habe ich jetzt so umgeschrieben! Danke dafür! Trotzdem hätte das
Problem weiterhin bestanden. Erst das Ersetzen des Template Parameters
durch den der Klasse hat den Konflikt behoben.
Falls es jemand mal braucht, hier ist die wohl endgültige Lösung:
Template Neuling schrieb:> Torsten R. schrieb:>> Du hier überhaupt eine Anwendung für `friend` in Deinem>> code. Friend deklariert eine Funktion (nicht member), die auf die>> privaten member eines Objekts zugreifen darf. Wenn operator[] bei Deiner>> Klasse nicht privat ist, gibt es exakt gar keinen Grund, den operator+>> zum friend zu deklarieren.>> Man kann den operator nicht mit zwei Variablen und als nicht friend> deklarieren, sonst kommt der Fehler "...operator... must take either> zero or one argument"
Och meno!!! Wenn ich schreibe, dass Du den Operator als _freie_
Funktion deklarieren sollst, dann sollst Du dass natürlich ausserhalb
(z.B. nach) der Klassendefinition tun!.
Ein Operator der zwei Argumente vom selben Typen nimmt, ist meinst ganz
gut als __freie__ Funktion definiert, da dies auch Konvertierungen
(z.B. durch einen c'tor) auf der linken Seite des Operators erlaubt.
mfg Torsten
Durch die friend Definition in der Klasse wird die Funktion übrigens
automatisch zu einer freien Funktion. Da Memberfunktionen sowieso
Zugriff auf alle Member haben macht es natürlich keinen Sinn
Memberfunktionen als friend zu haben.
Sebastian V. schrieb:> Durch die friend Definition in der Klasse wird die Funktion übrigens> automatisch zu einer freien Funktion. Da Memberfunktionen sowieso> Zugriff auf alle Member haben macht es natürlich keinen Sinn> Memberfunktionen als friend zu haben.
Doch, das kann sinnvoll sein. Eine freie Funktion hat im Umkehrschluss
nämlich keinen Zugriff auf interne Member einer Klasse.
Hast du dir meinen Satz mit Verstand durchgelesen? Die Aussage war, dass
es keinen Sinn macht Memberfunktionen der eigenen Klasse zu befreunden.
Bei freien Funktionen oder Memberfunktionen anderer Klassen kann friend
natürlich schon Sinn machen.
Sebastian V. schrieb:> Hast du dir meinen Satz mit Verstand durchgelesen? Die Aussage war, dass> es keinen Sinn macht Memberfunktionen der eigenen Klasse zu befreunden.
Doch, das ergibt durchaus Sinn, der hier und da einen Trick erlaubt.
Pratische Anwendung: https://woboq.com/blog/q_enum.html
Man mag jetzt natürlich über Sinn und Unsinn von Codegeneratoren und dem
MOC-Kram philosophieren.
Nase schrieb:> Sebastian V. schrieb:>> Hast du dir meinen Satz mit Verstand durchgelesen? Die Aussage war, dass>> es keinen Sinn macht Memberfunktionen der eigenen Klasse zu befreunden.> Doch, das ergibt durchaus Sinn, der hier und da einen Trick erlaubt.
Klar, es ändert sich semantisch nichts.
Der Trick besteht halt darin, die friend-Funktion in die Klasse selbst
zu inlinen, was ja per Makro machbar ist.
In deinem Beispiel wird doch auch davon geredet, dass die friend
Funktion eben keine Memberfunktion ist. Gibt sogar ein Beispiel aus dem
C++ Standard. §11.3 Abs. 6:
1
classM{
2
friendvoidf(){}// definition of global f, a friend of M,
3
// not the definition of a member function
4
};
Hier ruft man die Funktion einfach durch f() auf. Nicht M::f() und auch
nicht m.f() (mit m ein Objekt der Klasse M) oder irgendwas. Hier ist f
eine freie Funktion!
Sebastian V. schrieb:> In deinem Beispiel wird doch auch davon geredet, dass die friend> Funktion eben keine Memberfunktion ist. Gibt sogar ein Beispiel aus dem> C++ Standard. §11.3 Abs. 6:> class M {> friend void f() { } // definition of global f, a friend of M,> // not the definition of a member function> };> Hier ruft man die Funktion einfach durch f() auf.
Nein, dort ruft sie garnichts auf. Mehr noch, die Funktion f() _kann
garnicht_ aufgerufen werden, denn sie ist nirgendwo sichtbar.
Probiers aus.
1
#include<iostream>
2
usingnamespacestd;
3
4
classM{
5
friendvoidf(){
6
cout<<"f()"<<endl;
7
}
8
};
9
10
intmain(){
11
f();
12
}
1
test.c++: In function ‘int main()’:
2
test.c++:12:4: error: ‘f’ was not declared in this scope
3
f();
4
^
> Hier ist f eine freie Funktion!
Ja. Aber eine, die nicht aufgerufen werden kann, weil sie nirgendwo
sichtbar ist. Sogar innerhalb der Klasse ist f() nicht sichtbar, denn
friend ist keine Deklaration.
Der Trick ist, die "inline-friend"-Funktion per ADL an der
Namensauflösung zu beteiligen. Das ist genau das, was bei Qt gemacht
wird.
Nase schrieb:>> Hier ist f eine freie Funktion!> Ja. Aber eine, die nicht aufgerufen werden kann, weil sie nirgendwo> sichtbar ist. Sogar innerhalb der Klasse ist f() nicht sichtbar, denn> friend ist keine Deklaration.
Wenn man sie außerhalb der Klasse deklariert, geht es aber. Ich wusste
bisher gar nicht, dass es möglich ist, in C++ eine Funktion zu
definieren, ohne sie dabei auch zu deklarieren. Mir wurde immer
"eingetrichtert", dass jede Definition immer automatisch auch eine
Deklaration ist.
Das man eine Deklaration außerhalb der Klasse braucht hat mich gestern
auch verwirrt. Können wir uns jetzt wenigstens einigen, dass meine
ursprüngliche Aussgabe stimmt? Nämlich, dass man Memberfunktionen der
eigenen Klasse nicht als friend haben kann weil das erstens keinen Sinn
macht und zweitens dafür scheinbar auch keine Syntax gibt.
Rolf M. schrieb:> Ich wusste> bisher gar nicht, dass es möglich ist, in C++ eine Funktion zu> definieren, ohne sie dabei auch zu deklarieren.
Ich habe diese Sichtweise auch bis gestern nicht so richtig
wahrgenommen...
Sebastian V. schrieb:> Können wir uns jetzt wenigstens einigen, dass meine> ursprüngliche Aussgabe stimmt? [...]
Ja, ich habe dir da ja im Prinzip auch nicht widersprochen.
Der einzige Knackpunkt war, dass ich die (innerhalb der Klasse)
vereinbarte friend-Funktion als Member aufgefasst habe, weil sie halt in
der Klasse vereinbart wurde.
Mit dieser neuen Sichtweise kann ich aber leben, ist irgendwie ja auch
sinnvoll.