web-dev-qa-db-fra.com

Est-il possible de déclarer une fonction ami statique?

Voici un exemple de code C++ qui compile et fonctionne correctement:

class A
{
public:
   A() {/* empty */}

private:
   friend void IncrementValue(A &);
   int value;
};

void IncrementValue(A & a)
{
   a.value++;
}   

int main(int, char **)
{
   A a;
   IncrementValue(a);
   return 0;
}

Ce que je voudrais faire, cependant, est de déclarer IncrementValue () comme statique, afin qu'il ne puisse pas être vu ou appelé à partir d'une autre unité de compilation:

static void IncrementValue(A & a)
{
    a.value++;
}

Cependant, cela me donne une erreur de compilation:

temp.cpp: In function ‘void IncrementValue(A&)’:
temp.cpp:12: error: ‘void IncrementValue(A&)’ was declared ‘extern’ and later ‘static’
temp.cpp:8: error: previous declaration of ‘void IncrementValue(A&)’

... et la modification de la déclaration de l'ami pour correspondre n'aide pas:

friend static void IncrementValue(A &);

... car cela donne cette erreur:

temp.cpp:8: error: storage class specifiers invalid in friend function declarations

Ma question est, existe-t-il un moyen en C++ d'avoir une fonction amie (non-méthode) qui est déclarée statique?

28
Jeremy Friesner

Citant N3691 - §11.3/4 [class.friend]

Une fonction déclarée pour la première fois dans une déclaration d'ami a un lien externe (3.5). Sinon, la fonction conserve sa liaison précédente (7.1.1).

Vous devez donc déclarer la fonction comme staticavant la déclarer comme friend. Cela peut être fait en ajoutant les déclarations suivantes au-dessus de la définition de A.

class A;  // forward declaration, required for following declaration
static void IncrementValue(A&); // the friend declaration will retain static linkage
22
Praetorian

Sûr. Lisez attentivement la deuxième ligne du message d'erreur: la fonction a été déclarée extern et plus tardstatic. Donc, tout ce que vous avez à faire est de le déclarer statique avant la déclaration d'amis:

class A;
static void IncrementValue(A&);

class A {
    // class definition, including friend declaration
};

static void IncrementValue(A&) {
    // code here, of course
}
17
Pete Becker

Bien que le prétorien réponse soit techniquement correct en ce qu'il répond à la question que vous avez explicitement posée, je pense que ce n'est pas une réponse utile en ce que ce qu'il propose est à la fois malsain et ne remplit pas non plus votre objectif déclaré de souhaiter pour définir une méthode qui ne peut être appelée que dans l'unité de traduction des classes d'amis.

Il y a deux problèmes avec sa solution. Tout d'abord, toute autre unité de traduction qui inclut l'en-tête contenant la définition de classe précédée par la déclaration de fonction statique échouera à la compilation en raison de l'erreur que la fonction amie déclarée statiquement n'est pas définie dans le module de traduction de référence. Et deuxièmement, le référencement TU peut éliminer cette erreur de compilation en définissant la fonction statiquement déclarée elle-même, et cette définition pourra accéder à toutes les données privées de la classe dont la fonction a été déclarée amie Cela suggère que les fonctions ami devraient toujours avoir la liaison publique qui est leur valeur par défaut, car cela empêche cette violation d'encapsulation potentielle en raison des définitions multiples d'une fonction de liaison publique étant une erreur de compilation.

Je crois que @engf était sur la bonne voie dans son commentaire sur votre question, vous avez besoin d'une classe d'amis définie dans la même unité de traduction que la classe à laquelle vous souhaitez qu'elle puisse accéder. Par exemple.

// A.h

class A
{
public:
   A() : _value(0) {}
private:
   int _value;
   friend struct A_Accessor;
};
// A.cpp

struct A_Accessor
{
   static void IncrementValue(A& a)
   {
      ++a._value;
   }
};


TEST(StaticInit, IncrementA)
{
   A a;
   A_Accessor::IncrementValue(a);
}

Cela définira IncrementValue de manière à lui permettre d'accéder aux données privées de A, mais ne peut pas être référencé depuis l'extérieur du module de traduction de A.

10
Neutrino