Folgendes Konstrukt macht mir Probleme, sobald ich die
Implementierung der friend Methode aus der H-Datei in die CPP-Datei
verlagere. Wenn die Implementierung der friend Methode
in der Headerdatei jedoch verbleibt kompiliert der vollständige
Code ohne Fehler und Warnungen im g++ (mingw).
Folgende Fehlermeldung erhalte ich,
wenn ich die Implementierung statt direkt in der Headerdatei
in einer cpp-Datei vornehme:
g++ -DDEBUG -DWIN32 -D_DEBUG -URELEASE -O0 -g3 -p -pg -Wall -c
-fmessage-length=0 -oBitfield.o ..\Bitfield.cpp
..\/Bitfield.h: In function `std::ostream& operator<<(std::ostream&,
const Bitfield::BitReference&)':
..\/Bitfield.h:77: error: `class Bitfield::BitReference' is private
..\Bitfield.cpp:83: error: within this context
Build error occurred, build is stopped
Time consumed: 906 ms.
So geht es: (für wie es nicht geht siehe weiter unten)
/**************************************/
class Bitfield{
private:
class BitReference{
private:
Bitfield &bf;
size_t index;
public:
BitReference(Bitfield& BF, size_t IDX);
friend std::ostream& operator<<(std::ostream& s, const
Bitfield::BitReference& bRef){
if (bRef.bf.getBit(bRef.index))
s << "1";
else
s << "0";
return s;
};
//[....]
};
//[....]
public:
Bitfield::BitReference operator[](const int x);
Bitfield::BitReference operator[](const size_t x);
bool operator[](const int x) const;
bool operator[](const size_t x) const;
//[....]
};
/**************************************/
Warum aber geht das so nicht?
/**************************************/
//bitfield.h
class Bitfield{
private:
class BitReference{
private:
Bitfield &bf;
size_t index;
public:
BitReference(Bitfield& BF, size_t IDX);
friend std::ostream& operator<<(std::ostream& s, const
Bitfield::BitReference& bRef);
//[....]
};
//[....]
public:
Bitfield::BitReference operator[](const int x);
Bitfield::BitReference operator[](const size_t x);
bool operator[](const int x) const;
bool operator[](const size_t x) const;
//[....]
};
/**************************************/
//bitfield.cpp
std::ostream& operator<<(std::ostream& s, const Bitfield::BitReference&
bRef) {
if (bRef.bf.getBit(bRef.index))
s << "1";
else
s << "0";
return s;
}
/**************************************/
void f();
class X { friend void f(); };
void f() { /* ... */ }
Ich fange damit an, wieso der zweite Ansatz nicht geht:
Die friend-Deklaration in Bitfield::BitReference erteilt einer
Funktion
std::ostream& operator<<(std::ostream&, const
Bitfield::BitReference&);
die sich im nächsten umhüllenden Namensraum (hier also nur
der globale) das Zugriffsrecht auf private Teile von
Bitfield::BitReference. Allerdings gibt es nun noch das Problem,
dass Bitfield::BitReference *selbst* wieder ein privater Member
von Bitfield ist. Mit welchem Zugriffsrecht aber
Bitfield::BitReference
aus Bitfield heraus verfügbar ist, kann Bitfield::BitReference nicht
bestimmen und das ist auch gut so. Damit das geht, muss
*auch* die Klasse Bitfield der gleichen Funktion Zugriff gewären,
was du also durch eine wiederholte Friend-Deklaration in
BitField erreichst:
class Bitfield{
class BitReference;
friend std::ostream& operator<<(std::ostream&, const BitReference&);
...
};
oder
class Bitfield{
class BitReference {
...
friend std::ostream& operator<<(std::ostream&, const
BitReference&);
}
friend std::ostream& operator<<(std::ostream&, const BitReference&);
...
};
Die anderen Frage ist natürlich: Wieso geht das mit einer einzigen
friend-Deklaration (inklusive Definition) in deiner ersten Fassung?
Dies ist auf eine sehr alte friend-Regel in C++ zurückzuführen, die
es in dieser Frühzeit ermöglichte, die Abwesenheit von Funktions-
templates in gewisser Form auszugleichen. Wenn eine friend-Deklaration
in einer Klasse gleichzeitig eine Definition ist, dann wird diese
Funktion deklarativ in dem umgebenden Namensraum latent injiziert,
es handelt sich um keine Member-Funktion, und sie ist nur
verfügbar, wenn sie über nicht-qualifizierten Lookup gefunden wird.
D.h. du kannst sie nicht über :: aufrufen, wie z.B. in
::operator<<(/Argumente/);
sondern sie kann nur durch ADL gefunden werden. Da ADL für
argument-dependent-lookup steht, bedeutet das konsequenterweise,
dass mindestens einer der Funktionsparameter vom den Typ
der friend-deklarierenden Klasse referenzieren muss (vereinfacht
ausgedrückt). Bei dir sind diese Kriterien erfüllt, da einer der
Parameter vom Type Bitfield::BitReference ist. Die Funktion würde
aber nicht beim Aufruf gefunden werden, wenn sie z.B. die Deklaration
friend std::ostream& operator<<(std::ostream&, int);
hätte.
In der Hoffnung, dass dies damit etwas klarer wurde mit
Bestem Gruss aus Bremen,
Daniel Krügler
Eben das geht in seinem Beispiel nicht, da f als Argument eine
Sub-Klasse von X erwartet. Ich bin da auch schon drᅵber gestolpert -
ohne je eine echte Lᅵsung gefunden zu haben.
Tja, wenn man Sub-Klassen forward deklarieren kᅵnnte...
MfG
Olaf Krzikalla
Daniel Krügler schrieb:
> On 7 Okt., 09:37, Robert Hartmann <Robert_Hartm...@gmx.net> wrote:
>> Hallo zusammen,
>>
[...]
Vielen Dank für die ausführliche und interessante Antwort.
Gruß Robert