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

La déclaration friend apparaît dans un corps de classe et accorde à une fonction ou à une autre classe l'accès aux membres privés et protégés de la classe où la déclaration friend apparaît.

Table des matières

Syntaxe

friend déclaration-de-fonction (1)
friend définition-de-fonction (2)
friend spécificateur-de-type-élaboré ; (3) (jusqu'en C++26)
friend spécificateur-de-type-simple ;

friend spécificateur-de-typename ;

(4) (depuis C++11)
(jusqu'en C++26)
friend liste-spécificateurs-type-ami ; (5) (depuis C++26)
1,2) Une déclaration de fonction amie.
3-5) Une déclaration friend de classe.
function-declaration - une déclaration de fonction
function-definition - une définition de fonction
elaborated-type-specifier - un spécificateur de type élaboré
simple-type-specifier - un spécificateur de type simple
typename-specifier - le mot-clé typename suivi d'un identifiant qualifié ou d'un identifiant de modèle simple qualifié
friend-type-specifier-list - une liste non-vide séparée par des virgules de simple-type-specifier , elaborated-type-specifier , et typename-specifier s, chaque spécificateur pouvant être suivi par des points de suspension ( ... )

Description

1) Désigne une fonction ou plusieurs fonctions comme amies de cette classe :
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) (autorisé uniquement dans les définitions de classes non- locales ) Définit une fonction non-membre et la déclare simultanément comme amie de cette classe. Cette fonction non-membre est toujours inline , sauf si elle est attachée à un module nommé (depuis 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) Désigne une classe comme amie de cette classe. Cela signifie que les déclarations et définitions des membres de l'ami peuvent accéder aux membres privés et protégés de cette classe et que l'ami peut également hériter des membres privés et protégés de cette classe.
3) La classe est nommée par elaborated-type-specifier . Le nom de la classe utilisé dans cette déclaration d'amitié n'a pas besoin d'être préalablement déclaré.
4) La classe est nommée par simple-type-specifier ou typename-specifier . Si le type nommé n'est pas un type classe, cette déclaration d'amitié est ignorée. Cette déclaration ne déclarera pas anticipément un nouveau type.
5) Désigne toutes les classes dans friend-type-specifier-list comme amies de cette classe. Cela signifie que les déclarations et définitions des membres des amis peuvent accéder aux membres privés et protégés de cette classe et que les amis peuvent hériter des membres privés et protégés de cette classe. Si un type nommé n'est pas un type classe, il est ignoré dans cette déclaration d'amitié.
Chaque spécificateur dans friend-type-specifier-list nomme une classe si le spécificateur n'est pas suivi par des points de suspension, sinon l'expansion de paquet s'applique.
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
};

Amis de modèles

Les déclarations de function template et de class template peuvent apparaître avec le spécificateur friend dans toute classe ou class template non locale (bien que seuls les function templates puissent être définis dans la classe ou class template qui accorde l'amitié). Dans ce cas, chaque spécialisation du template devient un ami, qu'elle soit instanciée implicitement, partiellement spécialisée ou explicitement spécialisée.

class A
{
    template<typename T>
    friend class B; // chaque B<T> est un ami de A
    template<typename T>
    friend void f(T) {} // chaque f<T> est un ami de A
};

Les déclarations friend ne peuvent pas faire référence à des spécialisations partielles, mais peuvent faire référence à des spécialisations complètes :

template<class T>
class A {};      // primaire
template<class T>
class A<T*> {};  // partielle
template<>
class A<int> {}; // complète
class X
{
    template<class T>
    friend class A<T*>;  // Erreur
    friend class A<int>; // OK
};

Lorsqu'une déclaration friend fait référence à une spécialisation complète d'un modèle de fonction, les mots-clés inline , constexpr (depuis C++11) , consteval (depuis C++20) et les arguments par défaut ne peuvent pas être utilisés :

template<class T>
void f(int);
template<>
void f<int>(int);
class X
{
    friend void f<int>(int x = 1); // erreur : arguments par défaut non autorisés
};

Une déclaration d'ami template peut nommer un membre d'un template de classe A, qui peut être soit une fonction membre soit un type membre (le type doit utiliser un spécificateur de type élaboré ). Une telle déclaration n'est bien formée que si le dernier composant de son spécificateur de nom imbriqué (le nom à gauche du dernier :: ) est un simple-template-id (nom du template suivi d'une liste d'arguments entre chevrons) qui nomme le template de classe. Les paramètres template d'une telle déclaration d'ami template doivent être déductibles à partir du simple-template-id.

Dans ce cas, le membre de toute spécialisation de A ou des spécialisations partielles de A devient un ami. Cela n'implique pas l'instanciation du modèle primaire A ou des spécialisations partielles de A : les seules exigences sont que la déduction des paramètres de template de A à partir de cette spécialisation réussisse, et que la substitution des arguments de template déduits dans la déclaration friend produise une déclaration qui serait une redéclaration valide du membre de la spécialisation :

// modèle primaire
template<class T>
struct A
{ 
    struct B {};
    void f();
    struct D { void g(); };
    T h();
    template<T U>
    T i();
};
// spécialisation complète
template<>
struct A<int>
{
    struct B {};
    int f();
    struct D { void g(); };
    template<int U>
    int i();
};
// autre spécialisation complète
template<>
struct A<float*>
{
    int *h();
};
// la classe non-modèle accordant l'amitié aux membres du modèle de classe A
class X
{
    template<class T>
    friend struct A<T>::B; // tous les A<T>::B sont amis, y compris A<int>::B
    template<class T>
    friend void A<T>::f(); // A<int>::f() n'est pas ami car sa signature
                           // ne correspond pas, mais par exemple A<char>::f() est ami
//  template<class T>
//  friend void A<T>::D::g(); // mal formé, la dernière partie du spécificateur de nom imbriqué,
//                            // D dans A<T>::D::, n'est pas un simple-template-id
    template<class T>
    friend int* A<T*>::h(); // tous les A<T*>::h sont amis :
                            // A<float*>::h(), A<int*>::h(), etc
    template<class T> 
    template<T U>       // toutes les instanciations de A<T>::i() et A<int>::i() sont amies,
    friend T A<T>::i(); // et par conséquent toutes les spécialisations de ces modèles de fonction
};

Les arguments de template par défaut ne sont autorisés dans les déclarations de fonctions amies template que si la déclaration est une définition et qu'aucune autre déclaration de cette fonction template n'apparaît dans cette unité de traduction.

(depuis C++11)

Opérateurs friend template

Un cas d'utilisation courant pour les fonctions amies templates est la déclaration d'une surcharge d'opérateur non-membre qui agit sur un modèle de classe, par exemple operator << ( std:: ostream & , const Foo < T > & ) pour un Foo < T > défini par l'utilisateur.

Un tel opérateur peut être défini dans le corps de la classe, ce qui a pour effet de générer un operator << non-template distinct pour chaque T et fait de ce operator << non-template un ami de sa Foo < T > :

#include <iostream>
template<typename T>
class Foo
{
public:
    Foo(const T& val) : data(val) {}
private:
    T data;
    // génère un operator<< non-template pour ce 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';
}

Sortie :

1.23

ou la fonction modèle doit être déclarée comme un modèle avant le corps de la classe, auquel cas la déclaration d'amitié dans Foo < T > peut se référer à la spécialisation complète de operator << pour son T :

#include <iostream>
template<typename T>
class Foo; // déclaration anticipée pour permettre la déclaration de fonction
template<typename T> // déclaration
std::ostream& operator<<(std::ostream&, const Foo<T>&);
template<typename T>
class Foo
{
public:
    Foo(const T& val) : data(val) {}
private:
    T data;
    // fait référence à une spécialisation complète pour ce T particulier
    friend std::ostream& operator<< <> (std::ostream&, const Foo&);
    // note : cela repose sur la déduction d'arguments template dans les déclarations
    // peut également spécifier l'argument template avec operator<< <T>
};
// définition
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';
}

Liaison

Les spécificateurs de classe de stockage ne sont pas autorisés dans les déclarations friend.

Si une fonction ou un modèle de fonction est d'abord déclaré et défini dans une déclaration friend, et que la classe englobante est définie dans une déclaration d'exportation , son nom a la même liaison que le nom de la classe englobante.

(since C++20)

Si (jusqu'en C++20) Sinon, si (depuis C++20) une fonction ou un modèle de fonction est déclaré dans une déclaration friend, et qu'une déclaration non-friend correspondante est accessible, le nom possède la liaison déterminée par cette déclaration antérieure.

Sinon, la liaison du nom introduit par une déclaration friend est déterminée comme d'habitude.

Notes

L'amitié n'est pas transitive (l'ami de votre ami n'est pas votre ami).

L'amitié n'est pas héritée (les enfants de vos amis ne sont pas vos amis, et vos amis ne sont pas les amis de vos enfants).

Spécificateurs d'accès n'ont aucun effet sur la signification des déclarations friend (elles peuvent apparaître dans private : ou dans public : sections, sans aucune différence).

Une déclaration de classe friend ne peut pas définir une nouvelle classe ( friend class X { } ; est une erreur).

Lorsqu'une classe locale déclare une fonction ou une classe non qualifiée comme ami, seules les fonctions et classes dans la portée non-classe la plus interne sont recherchées , et non les fonctions globales :

class F {};
int f();
int main()
{
    extern int g();
    class Local // Classe locale dans la fonction main()
    {
        friend int f(); // Erreur, aucune fonction de ce type déclarée dans main()
        friend int g(); // OK, il y a une déclaration pour g dans main()
        friend class F; // déclare ami une F locale (définie plus tard)
        friend class ::F; // déclare ami la F globale
    };
    class F {}; // F locale
}

Un nom d'abord déclaré dans une déclaration friend à l'intérieur d'une classe ou d'un modèle de classe X devient un membre de l'espace de noms englobant le plus interne de X , mais n'est pas visible pour la recherche (sauf pour la recherche dépendante des arguments qui considère X ) à moins qu'une déclaration correspondante au niveau de l'espace de noms soit fournie - voir namespaces pour plus de détails.

Macro de test de fonctionnalité Valeur Std Fonctionnalité
__cpp_variadic_friend 202403L (C++26) Déclarations friend variadiques

Mots-clés

friend

Exemple

Les opérateurs d'insertion et d'extraction de flux sont souvent déclarés comme des fonctions amies non-membres :

#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';
}

Sortie :

MyClass::id = 6; i = 7
MyClass::id = 6; i = 100
MyClass::id = 9; i = 100

Rapports de défauts

Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.

DR Appliqué à Comportement publié Comportement corrigé
CWG 45 C++98 les membres d'une classe imbriquée dans une classe amie
de T n'ont pas d'accès spécial à T
une classe imbriquée a le même
accès que la classe englobante
CWG 500 C++98 une classe amie de T ne peut pas hériter des membres privés ou
protégés de T , mais sa classe imbriquée le peut
les deux peuvent hériter
de ces membres
CWG 1439 C++98 la règle visant les déclarations d'amitié dans les classes non locales
ne couvrait pas les déclarations de templates
couvert
CWG 1477 C++98 un nom d'abord déclaré dans une déclaration d'amitié à l'intérieur d'une classe
ou d'un template de classe n'était pas visible pour la recherche
si la déclaration correspondante est fournie dans une autre portée de namespace
il est visible pour
la recherche dans ce cas
CWG 1804 C++98 lorsqu'un membre d'un template de classe est déclaré ami, le membre correspondant
des spécialisations des spécialisations partielles du template de classe
n'était pas un ami de la classe accordant l'amitié
ces membres
sont aussi amis
CWG 2379 C++11 les déclarations d'amitié référençant les spécialisations complètes
des templates de fonction pouvaient être déclarées constexpr
interdit
CWG 2588 C++98 les linkages des noms introduits par les déclarations d'amitié étaient peu clairs clarifié

Références

  • Norme C++23 (ISO/CEI 14882:2024) :
  • 11.8.4 Amis [class.friend]
  • 13.7.5 Amis [temp.friend]
  • Norme C++20 (ISO/CEI 14882:2020) :
  • 11.9.3 Amis [class.friend]
  • 13.7.4 Amis [temp.friend]
  • Norme C++17 (ISO/IEC 14882:2017) :
  • 14.3 Amis [class.friend]
  • 17.5.4 Amis [temp.friend]
  • Norme C++14 (ISO/CEI 14882:2014) :
  • 11.3 Amis [class.friend]
  • 14.5.4 Amis [temp.friend]
  • Norme C++11 (ISO/IEC 14882:2011) :
  • 11.3 Amis [class.friend]
  • 14.5.4 Amis [temp.friend]
  • Norme C++98 (ISO/CEI 14882:1998) :
  • 11.3 Amis [class.friend]
  • 14.5.3 Amis [temp.friend]

Voir aussi

Class types définit les types contenant plusieurs membres de données
Access specifiers définit la visibilité des membres de classe