Friend declaration
Die Friend-Deklaration erscheint in einem Klassenkörper und gewährt einer Funktion oder einer anderen Klasse Zugriff auf private und protected Member der Klasse, in der die Friend-Deklaration erscheint.
Inhaltsverzeichnis |
Syntax
friend
Funktionsdeklaration
|
(1) | ||||||||
friend
Funktionsdefinition
|
(2) | ||||||||
friend
elaborated-type-specifier
;
|
(3) | (bis C++26) | |||||||
friend
simple-type-specifier
;
|
(4) |
(seit C++11)
(bis C++26) |
|||||||
friend
friend-type-specifier-list
;
|
(5) | (seit C++26) | |||||||
| function-declaration | - | eine Funktionsdeklaration |
| function-definition | - | eine Funktionsdefinition |
| elaborated-type-specifier | - | einen elaborated type specifier |
| simple-type-specifier | - | einen simple type specifier |
| typename-specifier | - | das Schlüsselwort typename gefolgt von einer qualifizierten Identifikation oder qualifizierten simple template identifier |
| friend-type-specifier-list | - |
eine nicht-leere, kommagetrennte Liste von
simple-type-specifier
,
elaborated-type-specifier
, und
typename-specifier
s, jeder Spezifizierer kann von einer Ellipse (
...
) gefolgt werden
|
Beschreibung
class Y { int data; // private member // the non-member function operator<< will have access to Y's private members friend std::ostream& operator<<(std::ostream& out, const Y& o); friend char* X::foo(int); // members of other classes can be friends too friend X::X(char), X::~X(); // constructors and destructors can be friends }; // friend declaration does not declare a member function // this operator<< still needs to be defined, as a non-member std::ostream& operator<<(std::ostream& out, const Y& y) { return out << y.data; // can access private member Y::data }
class X { int a; friend void friend_set(X& p, int i) { p.a = i; // this is a non-member function } public: void member_set(int i) { a = i; // this is a member function } };
class Y {}; class A { int data; // private data member class B {}; // private nested type enum { a = 100 }; // private enumerator friend class X; // friend class forward declaration (elaborated class specifier) friend Y; // friend class declaration (simple type specifier) (since C++11) // the two friend declarations above can be merged since C++26: // friend class X, Y; }; class X : A::B // OK: A::B accessible to friend { A::B mx; // OK: A::B accessible to member of friend class Y { A::B my; // OK: A::B accessible to nested member of friend }; int v[A::a]; // OK: A::a accessible to member of friend };
Template-Freunde
Sowohl
function template
als auch
class template
Deklarationen können mit dem
friend
Spezifizierer in jeder nicht-lokalen Klasse oder Klassenvorlage erscheinen (obwohl nur Funktionsvorlagen innerhalb der Klasse oder Klassenvorlage, die Freundschaft gewährt, definiert werden können). In diesem Fall wird jede Spezialisierung der Vorlage zu einem Freund, unabhängig davon, ob sie implizit instanziiert, partiell spezialisiert oder explizit spezialisiert wird.
class A { template<typename T> friend class B; // jedes B<T> ist ein friend von A template<typename T> friend void f(T) {} // jedes f<T> ist ein friend von A };
Friend-Deklarationen können nicht auf partielle Spezialisierungen verweisen, aber auf vollständige Spezialisierungen:
template<class T> class A {}; // primär template<class T> class A<T*> {}; // partiell template<> class A<int> {}; // vollständig class X { template<class T> friend class A<T*>; // Fehler friend class A<int>; // OK };
Wenn eine Friend-Deklaration auf eine vollständige Spezialisierung einer Funktionsvorlage verweist, können die Schlüsselwörter inline , constexpr (since C++11) , consteval (since C++20) und Default-Argumente nicht verwendet werden:
template<class T> void f(int); template<> void f<int>(int); class X { friend void f<int>(int x = 1); // Fehler: Standardargumente nicht erlaubt };
Eine Template-Friend-Deklaration kann ein Mitglied einer Klassentemplate A benennen, das entweder eine Memberfunktion oder ein Membertyp sein kann (der Typ muss einen
elaborated-type-specifier
verwenden). Eine solche Deklaration ist nur wohlgeformt, wenn die letzte Komponente in ihrer geschachtelten-Namen-Spezifikation (der Name links vom letzten
::
) eine simple-template-id (Template-Name gefolgt von Argumentliste in spitzen Klammern) ist, die das Klassentemplate benennt. Die Template-Parameter einer solchen Template-Friend-Deklaration müssen von der simple-template-id ableitbar sein.
In diesem Fall wird das Mitglied jeder Spezialisierung von A oder partiellen Spezialisierungen von A zu einem Friend. Dies beinhaltet nicht die Instanziierung des primären Templates A oder partieller Spezialisierungen von A: Die einzigen Anforderungen sind, dass die Deduktion der Template-Parameter von A aus dieser Spezialisierung erfolgreich ist und dass die Substitution der deduzierten Template-Argumente in die Friend-Deklaration eine Deklaration erzeugt, die eine gültige Neudeklaration des Mitglieds der Spezialisierung wäre:
// Primäres Template template<class T> struct A { struct B {}; void f(); struct D { void g(); }; T h(); template<T U> T i(); }; // Vollständige Spezialisierung template<> struct A<int> { struct B {}; int f(); struct D { void g(); }; template<int U> int i(); }; // Weitere vollständige Spezialisierung template<> struct A<float*> { int *h(); }; // Die nicht-templatisierte Klasse, die Member der Klassentemplate A Freundschaft gewährt class X { template<class T> friend struct A<T>::B; // alle A<T>::B sind Freunde, einschließlich A<int>::B template<class T> friend void A<T>::f(); // A<int>::f() ist kein Freund, da seine Signatur // nicht übereinstimmt, aber z.B. A<char>::f() ist ein Freund // template<class T> // friend void A<T>::D::g(); // ungültig, der letzte Teil des geschachtelten Namensspezifizierers, // // D in A<T>::D::, ist keine einfache Template-ID template<class T> friend int* A<T*>::h(); // alle A<T*>::h sind Freunde: // A<float*>::h(), A<int*>::h(), etc template<class T> template<T U> // alle Instanziierungen von A<T>::i() und A<int>::i() sind Freunde, friend T A<T>::i(); // und damit alle Spezialisierungen dieser Funktions-Templates };
|
Standardmäßige Template-Argumente sind nur in Template-Friend-Deklarationen zulässig, wenn die Deklaration eine Definition ist und keine weiteren Deklarationen dieser Funktions-Template in dieser Übersetzungseinheit erscheinen. |
(seit C++11) |
Template-Freund-Operatoren
Ein häufiger Anwendungsfall für Template-Freunde ist die Deklaration einer Nicht-Member-Operatorüberladung, die auf einer Klassenvorlage operiert, z.B. operator << ( std:: ostream & , const Foo < T > & ) für eine benutzerdefinierte Foo < T > .
Ein solcher Operator kann im Klassenkörper definiert werden, was zur Folge hat, dass ein separater Nicht-Template-
operator
<<
für jedes
T
erzeugt wird und dieser Nicht-Template-
operator
<<
ein Freund seiner
Foo
<
T
>
wird:
#include <iostream> template<typename T> class Foo { public: Foo(const T& val) : data(val) {} private: T data; // generates a non-template operator<< for this T friend std::ostream& operator<<(std::ostream& os, const Foo& obj) { return os << obj.data; } }; int main() { Foo<double> obj(1.23); std::cout << obj << '\n'; }
Ausgabe:
1.23
oder die Funktionsvorlage muss vor dem Klassenrumpf als Vorlage deklariert werden, in diesem Fall kann die Friend-Deklaration innerhalb von
Foo
<
T
>
auf die Vollständige Spezialisierung von
operator
<<
für ihren
T
verweisen:
#include <iostream> template<typename T> class Foo; // Vorwärtsdeklaration zur Ermöglichung der Funktionsdeklaration template<typename T> // Deklaration std::ostream& operator<<(std::ostream&, const Foo<T>&); template<typename T> class Foo { public: Foo(const T& val) : data(val) {} private: T data; // bezieht sich auf eine vollständige Spezialisierung für dieses spezifische T friend std::ostream& operator<< <> (std::ostream&, const Foo&); // Hinweis: dies basiert auf Template-Argumentableitung in Deklarationen // kann auch das Template-Argument mit operator<< <T> angeben }; // Definition template<typename T> std::ostream& operator<<(std::ostream& os, const Foo<T>& obj) { return os << obj.data; } int main() { Foo<double> obj(1.23); std::cout << obj << '\n'; }
Verknüpfung
Spezifizierer der Speicherklasse sind in Friend-Deklarationen nicht zulässig.
|
Wenn eine Funktion oder Funktionsvorlage zuerst in einer Friend-Deklaration deklariert und definiert wird und die einschließende Klasse innerhalb einer exportierenden Deklarationen definiert ist, hat ihr Name dieselbe Verknüpfung wie der Name der einschließenden Klasse. |
(since C++20) |
Wenn (bis C++20) Andernfalls, wenn (seit C++20) eine Funktion oder Funktionsvorlage in einer Friend-Deklaration deklariert wird und eine entsprechende Nicht-Friend-Deklaration erreichbar ist, hat der Name die Verknüpfung, die von dieser vorherigen Deklaration bestimmt wird.
Andernfalls wird die Verknüpfung des durch eine Friend-Deklaration eingeführten Namens wie üblich bestimmt.
Hinweise
Freundschaft ist nicht transitiv (ein Freund deines Freundes ist nicht dein Freund).
Freundschaft wird nicht vererbt (die Kinder Ihrer Freunde sind nicht Ihre Freunde, und Ihre Freunde sind nicht die Freunde Ihrer Kinder).
Zugriffsspezifizierer haben keine Auswirkung auf die Bedeutung von Friend-Deklarationen (sie können in private : oder in public : Abschnitten erscheinen, ohne Unterschied).
Eine Friend-Klassendeklaration kann keine neue Klasse definieren ( friend class X { } ; ist ein Fehler).
Wenn eine lokale Klasse eine unqualifizierte Funktion oder Klasse als friend deklariert, werden nur Funktionen und Klassen im innersten Nicht-Klassen-Bereich gesucht , nicht die globalen Funktionen:
class F {}; int f(); int main() { extern int g(); class Local // Lokale Klasse in der main()-Funktion { friend int f(); // Fehler, keine solche Funktion in main() deklariert friend int g(); // OK, es gibt eine Deklaration für g in main() friend class F; // befriended ein lokales F (später definiert) friend class ::F; // befriended das globale F }; class F {}; // lokales F }
Ein Name, der zuerst in einer Friend-Deklaration innerhalb einer Klasse oder Klassenvorlage
X
deklariert wird, wird zu einem Mitglied des innersten umschließenden Namensraums von
X
, ist jedoch für die Suche nicht sichtbar (außer bei argumentabhängiger Suche, die
X
berücksichtigt), es sei denn, eine entsprechende Deklaration im Namensraumbereich wird bereitgestellt - siehe
Namensräume
für Details.
| Feature-Test-Makro | Wert | Std | Feature |
|---|---|---|---|
__cpp_variadic_friend
|
202403L
|
(C++26) | Variadische Friend-Deklarationen |
Schlüsselwörter
Beispiel
Stream-Einfüge- und Extraktionsoperatoren werden oft als nicht-Member-Freunde deklariert:
#include <iostream> #include <sstream> class MyClass { int i; // friends have access to non-public, non-static static inline int id{6}; // and static (possibly inline) members friend std::ostream& operator<<(std::ostream& out, const MyClass&); friend std::istream& operator>>(std::istream& in, MyClass&); friend void change_id(int); public: MyClass(int i = 0) : i(i) {} }; std::ostream& operator<<(std::ostream& out, const MyClass& mc) { return out << "MyClass::id = " << MyClass::id << "; i = " << mc.i; } std::istream& operator>>(std::istream& in, MyClass& mc) { return in >> mc.i; } void change_id(int id) { MyClass::id = id; } int main() { MyClass mc(7); std::cout << mc << '\n'; // mc.i = 333*2; // error: i is a private member std::istringstream("100") >> mc; std::cout << mc << '\n'; // MyClass::id = 222*3; // error: id is a private member change_id(9); std::cout << mc << '\n'; }
Ausgabe:
MyClass::id = 6; i = 7 MyClass::id = 6; i = 100 MyClass::id = 9; i = 100
Fehlerberichte
Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | Angewendet auf | Verhalten wie veröffentlicht | Korrektes Verhalten |
|---|---|---|---|
| CWG 45 | C++98 |
Member einer Klasse, die in einer Friend-Klasse
von
T
geschachtelt ist, haben keinen speziellen Zugriff auf
T
|
eine geschachtelte Klasse hat denselben
Zugriff wie die umschließende Klasse |
| CWG 500 | C++98 |
Friend-Klasse von
T
kann nicht von privaten oder
geschützten Membern von
T
erben, aber ihre geschachtelte Klasse kann
|
beide können von solchen
Membern erben |
| CWG 1439 | C++98 |
die Regel für Friend-Deklarationen in nicht-lokalen
Klassen deckte Template-Deklarationen nicht ab |
abgedeckt |
| CWG 1477 | C++98 |
ein Name, der zuerst in einer Friend-Deklaration innerhalb einer Klasse
oder Klassentemplate deklariert wurde, war für die Suche nicht sichtbar, wenn die passende Deklaration in einem anderen Namensbereich bereitgestellt wurde |
er ist in diesem Fall für
die Suche sichtbar |
| CWG 1804 | C++98 |
wenn ein Member eines Klassentemplates als Friend deklariert wird, war der entsprechende
Member von Spezialisierungen von partiellen Spezialisierungen des Klassentemplates kein Friend der Klasse, die die Freundschaft gewährt |
solche Member
sind ebenfalls Friends |
| CWG 2379 | C++11 |
Friend-Deklarationen, die sich auf vollständige Spezialisierungen
von Funktionstemplates beziehen, konnten als constexpr deklariert werden |
verboten |
| CWG 2588 | C++98 | die Verknüpfungen von Namen, die durch Friend-Deklarationen eingeführt wurden, waren unklar | klargestellt |
Referenzen
- C++23-Standard (ISO/IEC 14882:2024):
-
- 11.8.4 Friends [class.friend]
-
- 13.7.5 Friends [temp.friend]
- C++20-Standard (ISO/IEC 14882:2020):
-
- 11.9.3 Friends [class.friend]
-
- 13.7.4 Friends [temp.friend]
- C++17-Standard (ISO/IEC 14882:2017):
-
- 14.3 Friends [class.friend]
-
- 17.5.4 Friends [temp.friend]
- C++14-Standard (ISO/IEC 14882:2014):
-
- 11.3 Friends [class.friend]
-
- 14.5.4 Friends [temp.friend]
- C++11-Standard (ISO/IEC 14882:2011):
-
- 11.3 Friends [class.friend]
-
- 14.5.4 Friends [temp.friend]
- C++98-Standard (ISO/IEC 14882:1998):
-
- 11.3 Friends [class.friend]
-
- 14.5.3 Friends [temp.friend]
Siehe auch
| Class types | definiert Typen, die mehrere Datenmember halten |
| Access specifiers | definiert die Sichtbarkeit von Klassenmembern |