Namensräume
Varianten
Aktionen

Friend-Deklaration

Von cppreference.com
< cpp‎ | Sprache
 
 
C++ Sprache
Allgemeine Themen
Kontrollfluss
Bedingte Ausführungsaussagen
if
Iterationsanweisungen (Schleifen)
for
Bereichs-for (C++11)
Sprunganweisungen
Funktionen
Funktionsdeklaration
Lambda-Funktionsausdruck
inline-Spezifizierer
Dynamische Ausnahmespezifikationen (bis C++17*)
noexcept-Spezifizierer (C++11)
Ausnahmen
Namensräume
Typen
Spezifizierer
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Speicherdauer-Spezifizierer
Initialisierung
Ausdrücke
Alternative Darstellungen
Literale
Boolesch - Ganzzahl - Gleitkommazahl
Zeichen - String - nullptr (C++11)
Benutzerdefinierte (C++11)
Dienstprogramme
Attribute (C++11)
Typen
typedef-Deklaration
Typalias-Deklaration (C++11)
Umwandlungen
Speicherzuweisung
Klassen
Zugriffsspezifizierer
friend-Spezifizierer

Klassenspezifische Funktionseigenschaften
explicit (C++11)
static

Spezielle Member-Funktionen
Templates
Sonstiges
 
 

Die Friend-Deklaration erscheint im 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.

Inhalt

[bearbeiten] Syntax

friend Funktionsdeklarator (1)
friend Funktionsdefinition (2)
friend elaborated-type-specifier ; (3) (bis C++26)
friend simple-type-specifier ;

friend typename-specifier ;

(4) (seit C++11)
(bis C++26)
friend friend-type-specifier-list ; (5) (seit C++26)
1,2) Eine Friend-Deklaration einer Funktion.
3-5) Eine Friend-Deklaration einer Klasse.
funktionsdeklarator - eine Funktionsdeklaration
funktionsdefinition - eine Funktionsdefinition
elaborated-type-specifier - ein elaborated type specifier
simple-type-specifier - ein simple type specifier
typename-specifier - das Schlüsselwort typename gefolgt von einem qualifizierten Bezeichner oder einem qualifizierten einfachen Template-Bezeichner
friend-type-specifier-list - eine nicht-leere, durch Kommas getrennte Liste von simple-type-specifier, elaborated-type-specifier und typename-specifier, wobei jeder Specifier von einer Ellipse (...) gefolgt sein kann

[bearbeiten] Beschreibung

1) Bezeichnet eine Funktion oder mehrere Funktionen als Freunde dieser Klasse
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
}
2) (nur in nicht-lokalen Klassendefinitionen erlaubt) Definiert eine nicht-Member-Funktion und macht sie gleichzeitig zu einem Freund dieser Klasse. Solch eine nicht-Member-Funktion ist immer inline, es sei denn, sie ist einem benannten Modul zugeordnet(seit C++20).
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
    }
};
3,4) Bezeichnet eine Klasse als Freund dieser Klasse. Das bedeutet, dass die Member-Deklarationen und -Definitionen des Freundes auf private und protected Member dieser Klasse zugreifen können und dass der Freund auch von privaten und protected Membern dieser Klasse erben kann.
3) Die Klasse wird durch elaborated-type-specifier benannt. Der Name der Klasse, der in dieser Friend-Deklaration verwendet wird, muss nicht zuvor deklariert worden sein.
4) Die Klasse wird durch simple-type-specifier oder typename-specifier benannt. Wenn der benannte Typ kein Klassentyp ist, wird diese Friend-Deklaration ignoriert. Diese Deklaration wird keinen neuen Typ vorwärts deklarieren.
5) Bezeichnet alle Klassen in friend-type-specifier-list als Freund dieser Klasse. Das bedeutet, dass die Member-Deklarationen und -Definitionen der Freunde auf private und protected Member dieser Klasse zugreifen können und dass die Freunde auch von privaten und protected Membern dieser Klasse erben können. Wenn ein benannter Typ kein Klassentyp ist, wird er in dieser Friend-Deklaration ignoriert.
Jeder Specifier in friend-type-specifier-list benennt eine Klasse, wenn der Specifier nicht von einer Ellipse gefolgt wird, andernfalls gilt Pack-Expansion.
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
};

[bearbeiten] Template-Freunde

Sowohl Funktionstemplates als auch Klassentemplates können mit dem Schlüsselwort friend in jeder nicht-lokalen Klasse oder jedem Klassentemplate deklariert werden (obwohl nur Funktionstemplates innerhalb der Klasse oder des Klassentemplates, das die Freundschaft gewährt, definiert werden dürfen). In diesem Fall wird jede Spezialisierung des Templates zu einem Freund, unabhängig davon, ob sie implizit instanziiert, partiell spezialisiert oder explizit spezialisiert wurde.

class A
{
    template<typename T>
    friend class B; // every B<T> is a friend of A
 
    template<typename T>
    friend void f(T) {} // every f<T> is a friend of A
};

Friend-Deklarationen können nicht auf partielle Spezialisierungen verweisen, aber auf vollständige Spezialisierungen

template<class T>
class A {};      // primary
 
template<class T>
class A<T*> {};  // partial
 
template<>
class A<int> {}; // full
 
class X
{
    template<class T>
    friend class A<T*>;  // Error
 
    friend class A<int>; // OK
};

Wenn eine Friend-Deklaration auf eine vollständige Spezialisierung eines Funktionstemplate verweist, können die Schlüsselwörter inline, constexpr(seit C++11), consteval(seit C++20) und Standardargumente nicht verwendet werden

template<class T>
void f(int);
 
template<>
void f<int>(int);
 
class X
{
    friend void f<int>(int x = 1); // error: default args not allowed
};

Eine Template-Friend-Deklaration kann ein Mitglied eines Klassentemplates A benennen, das entweder eine Member-Funktion oder ein Member-Typ sein kann (der Typ muss elaborated-type-specifier verwenden). Solch eine Deklaration ist nur dann wohlgeformt, wenn die letzte Komponente im nested-name-specifier (der Name links vom letzten ::) ein simple-template-id ist (Template-Name gefolgt von Argumentenliste in spitzen Klammern), das das Klassentemplate benennt. Die Template-Parameter einer solchen Template-Friend-Deklaration müssen aus dem simple-template-id ableitbar sein.

In diesem Fall wird das Mitglied jeder Spezialisierung von entweder A oder partiellen Spezialisierungen von A ein Freund. Dies beinhaltet nicht die Instanziierung des primären Templates A oder partieller Spezialisierungen von A: Die einzigen Anforderungen sind, dass die Ableitung der Template-Parameter von A aus dieser Spezialisierung erfolgreich ist und dass die Substitution der abgeleiteten Template-Argumente in die Friend-Deklaration eine Deklaration ergibt, die eine gültige Wiederholung des Mitglieds der Spezialisierung wäre.

// primary template
template<class T>
struct A
{ 
    struct B {};
 
    void f();
 
    struct D { void g(); };
 
    T h();
 
    template<T U>
    T i();
};
 
// full specialization
template<>
struct A<int>
{
    struct B {};
 
    int f();
 
    struct D { void g(); };
 
    template<int U>
    int i();
};
 
// another full specialization
template<>
struct A<float*>
{
    int *h();
};
 
// the non-template class granting friendship to members of class template A
class X
{
    template<class T>
    friend struct A<T>::B; // all A<T>::B are friends, including A<int>::B
 
    template<class T>
    friend void A<T>::f(); // A<int>::f() is not a friend because its signature
                           // does not match, but e.g. A<char>::f() is a friend
 
//  template<class T>
//  friend void A<T>::D::g(); // ill-formed, the last part of the nested-name-specifier,
//                            // D in A<T>::D::, is not simple-template-id
 
    template<class T>
    friend int* A<T*>::h(); // all A<T*>::h are friends:
                            // A<float*>::h(), A<int*>::h(), etc
 
    template<class T> 
    template<T U>       // all instantiations of A<T>::i() and A<int>::i() are friends, 
    friend T A<T>::i(); // and thereby all specializations of those function templates
};

Standard-Template-Argumente sind nur bei Template-Friend-Deklarationen erlaubt, wenn die Deklaration eine Definition ist und keine anderen Deklarationen dieses Funktionstemplate in dieser Translation Unit erscheinen.

(seit C++11)

[bearbeiten] Template-Friend-Operatoren

Ein gängiger Anwendungsfall für Template-Freunde ist die Deklaration einer Nicht-Member-Operatorüberladung, die auf ein Klassentemplate wirkt, z. B. operator<<(std::ostream&, const Foo<T>&) für ein benutzerdefiniertes Foo<T>.

Ein solcher Operator kann im Klassenkörper definiert werden, was den Effekt hat, dass für jedes T ein separater Nicht-Template operator<< generiert wird und dieser Nicht-Template operator<< zu einem Freund seines 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 das Funktionstemplates muss als Template vor dem Klassenkörper deklariert werden, in welchem Fall die Friend-Deklaration innerhalb von Foo<T> auf die vollständige Spezialisierung von operator<< für sein T verweisen kann

#include <iostream>
 
template<typename T>
class Foo; // forward declare to make function declaration possible
 
template<typename T> // declaration
std::ostream& operator<<(std::ostream&, const Foo<T>&);
 
template<typename T>
class Foo
{
public:
    Foo(const T& val) : data(val) {}
private:
    T data;
 
    // refers to a full specialization for this particular T 
    friend std::ostream& operator<< <> (std::ostream&, const Foo&);
 
    // note: this relies on template argument deduction in declarations
    // can also specify the template argument with operator<< <T>"
};
 
// 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';
}

[bearbeiten] Linkage

Storage-Klassenspezifizierer sind in Friend-Deklarationen nicht erlaubt.

Wenn eine Funktion oder ein Funktionstemplates zuerst in einer Friend-Deklaration deklariert und definiert wird und die umschließende Klasse innerhalb von exportierten Deklarationen definiert ist, hat ihr Name die gleiche Linkage wie der Name der umschließenden Klasse.

(seit C++20)

Wenn(bis C++20)Andernfalls, wenn(seit C++20) eine Funktion oder ein Funktionstemplates in einer Friend-Deklaration deklariert wird und eine entsprechende Nicht-Friend-Deklaration erreichbar ist, hat der Name die Linkage, die aus dieser vorherigen Deklaration bestimmt wird.

Andernfalls wird die Linkage des Namens, der durch eine Friend-Deklaration eingeführt wird, wie üblich bestimmt.

[bearbeiten] Notizen

Freundschaft ist nicht transitiv (ein Freund deines Freundes ist nicht dein Freund).

Freundschaft ist nicht vererbbar (die Kinder deines Freundes sind nicht deine Freunde, und deine Freunde sind nicht die Freunde deiner 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 nicht qualifizierte Funktion oder Klasse als Freund deklariert, werden nur Funktionen und Klassen im innersten nicht-Klassen-Gültigkeitsbereich gesucht, nicht aber globale Funktionen.

class F {};
 
int f();
 
int main()
{
    extern int g();
 
    class Local // Local class in the main() function
    {
        friend int f(); // Error, no such function declared in main()
        friend int g(); // OK, there is a declaration for g in main()
        friend class F; // friends a local F (defined later)
        friend class ::F; // friends the global F
    };
 
    class F {}; // local F
}

Ein Name, der zuerst in einer Friend-Deklaration innerhalb einer Klasse oder eines Klassentemplates X deklariert wird, wird Mitglied des innersten umschließenden Namensraums von X, ist aber für die Suche nicht sichtbar (außer bei Argument-abhängiger Suche, die X berücksichtigt), es sei denn, eine übereinstimmende Deklaration im Namensraum wird bereitgestellt - siehe Namensräume für Details.

Feature-Testmakro Wert Std Feature
__cpp_variadic_friend 202403L (C++26) Variadische Friend-Deklarationen

[bearbeiten] Schlüsselwörter

friend

[bearbeiten] 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

[bearbeiten] Fehlerberichte

Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.

DR angewendet auf Verhalten wie veröffentlicht Korrigiertes Verhalten
CWG 45 C++98 Mitglieder einer Klasse, die in einem Freund verschachtelt ist
Klasse von T haben keinen besonderen Zugriff auf T
eine verschachtelte Klasse hat die gleiche
Zugriffsberechtigung wie die umschließende Klasse
CWG 500 C++98 Friend-Klasse von T kann nicht von privaten oder
protected-Mitgliedern von T erben, aber ihre verschachtelte Klasse kann
beide können erben
von solchen Mitgliedern
CWG 1439 C++98 die Regel, die sich auf Friend-Deklarationen in nicht-lokalen
Klassen bezieht, deckte keine Template-Deklarationen ab
abgedeckt
CWG 1477 C++98 ein Name, der zuerst in einer Friend-Deklaration innerhalb einer Klasse
oder eines Klassentemplates deklariert wurde, war für die Suche nicht sichtbar, wenn die übereinstimmende
Deklaration in einem anderen Namensraum-Gültigkeitsbereich bereitgestellt wird
sie ist für die
Suche in diesem Fall sichtbar
CWG 1804 C++98 wenn ein Mitglied eines Klassentemplates befreundet wird, das entsprechende
Mitglied von Spezialisierungen von partiellen Spezialisierungen des Klassentemplates
war kein Freund der Klasse, die Freundschaft gewährt
solche Mitglieder
sind ebenfalls Freunde
CWG 2379 C++11 Friend-Deklarationen, die sich auf vollständige Spezialisierungen
von Funktionstemplates beziehen, könnten constexpr deklariert werden
verboten
CWG 2588 C++98 die Linkages von Namen, die durch Friend-Deklarationen eingeführt wurden, waren unklar wurde klargestellt

[bearbeiten] 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]

[bearbeiten] Siehe auch

Klassentypen definiert Typen, die mehrere Datenmitglieder enthalten [bearbeiten]
Zugriffsspezifizierer definiert die Sichtbarkeit von Klassenmitgliedern[bearbeiten]