Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Implementierung einer friend Methode für eine private Subklasse - warum geht das nicht so?

0 views
Skip to first unread message

Robert Hartmann

unread,
Oct 7, 2009, 3:37:25 AM10/7/09
to
Hallo zusammen,

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;
}
/**************************************/

Markus Schaaf

unread,
Oct 7, 2009, 9:05:06 AM10/7/09
to
Du musst die friend Methode auf Namespace-Ebene vor der
Klassendefinition deklarieren:

void f();
class X { friend void f(); };
void f() { /* ... */ }

Daniel Krügler

unread,
Oct 7, 2009, 9:32:51 AM10/7/09
to

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

Olaf Krzikalla

unread,
Oct 7, 2009, 10:24:20 AM10/7/09
to
Markus Schaaf schrieb:

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

Robert Hartmann

unread,
Oct 8, 2009, 4:26:31 AM10/8/09
to
Hallo Daniel,

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

0 new messages