Namespaces
Variants

Friend declaration

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Access specifiers
friend specifier

Class-specific function properties
Special member functions
Templates
Miscellaneous

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 ;

friend typename-specifier ;

(4) (seit C++11)
(bis C++26)
friend friend-type-specifier-list ; (5) (seit C++26)
1,2) Eine Friend-Funktionsdeklaration.
3-5) Eine Friend-Deklaration für eine Klasse.
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

1) Bestimmt eine Funktion oder mehrere Funktionen als Friends 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 erlaubt in nicht- lokalen Klassendefinitionen) Definiert eine Nicht-Member-Funktion und macht sie gleichzeitig zu einem friend dieser Klasse. Solche Nicht-Member-Funktionen sind immer inline , es sei denn, sie ist an ein named module gebunden (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) Erklärt eine Klasse als Friend dieser Klasse. Dies bedeutet, dass die Member-Deklarationen und Definitionen des Friends auf private und protected Member dieser Klasse zugreifen können und dass der Friend von privaten und protected Membern dieser Klasse erben kann.
3) Die Klasse wird durch den elaborated-type-specifier benannt. Der Name der Klasse, der in dieser Friend-Deklaration verwendet wird, muss nicht zuvor deklariert werden.
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 deklariert keinen neuen Typ vorwärts.
5) Erklärt alle Klassen in friend-type-specifier-list als Freund dieser Klasse. Dies bedeutet, dass die Member-Deklarationen und Definitionen der Freunde auf private und protected Member dieser Klasse zugreifen können und dass die Freunde 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 Spezifizierer in friend-type-specifier-list benennt eine Klasse, wenn der Spezifizierer nicht von einer Ellipse gefolgt wird, andernfalls findet Pack-Expansion Anwendung.
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

friend

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