2011-02-16 3 views
23

Bonjour J'ai ce code avec une erreur du compilateur (erreur de Microsoft Visual Studio 2008):une question sur la priorité de l ' « adresse » des opérateurs de C et « résolution de portée »

class B 
{ 
protected: 
int b; 
}; 

class A : public B 
{ 
public: 
void foo(){ &B::b; }// error C2248: 'B::b' : cannot access protected member declared in class 'B' 
}; 

alors que ce code est sans erreur:

class B 
{ 
protected: 
int b; 
}; 

class A : public B 
{ 
public: 
void foo(){ &(B::b); } 
}; 

les deux extraits me semble équivalent basé sur ma connaissance de la priorité des opérateurs, car :: a une priorité plus élevée que & (voir par exemple le tableau 2, à la page 137 du « COMBAT JOINT STRIKE FIGHTER NORMES DE CODAGE C++ DU VEHICULE AERIEN POUR LE DEVELOPPEMENT DU SYSTEME A Mais ils sont différents ... Je pense que c'est quelque chose en rapport avec "pointer-to-data-member" mais je ne sais pas comment ça va avec la priorité des opérateurs.

Une explication?

Merci, Alessandro

+0

Si la préséance était erronée alors ce serait sûrement une erreur de syntaxe (différente)? – Flexo

+2

Notez la différence: 'int * i = & (A :: b);' mais 'int A :: * m = &A::b;' –

Répondre

13

Dans le premier cas, vous prenez l'adresse de pointeur à membre B::b. Comme un tel pointeur n'est pas un membre du parent de A mais un objet séparé, il ne peut pas y accéder via le mécanisme protégé.

Dans le second cas où cela fonctionne vous demandez l'adresse du de b, instance spécifique qualification avec sa classe de base de telle sorte que dans le cas d'héritage multiple le compilateur savoir quelle classe de base que vous voulez dire . Dans ce contexte, l'attribut protégé est visible.

Notez que cette compile:

class B 
{ 
protected: 
int b; 
}; 

class A : public B 
{ 
public: 
void foo(){ &A::b; } // Note here &A:: instead of &B:: 
}; 

À titre d'exemple ajouté qu'il ne fonctionne pas pour la même raison que le code suivant (je l'espère plus familier) ne fonctionne pas:

class B 
{ 
protected: 
int b; 
}; 

class A : public B 
{ 
public: 
void foo(const B* b_obj) { b_obj->b; } 
}; 
+1

il compilera également '& this-> B :: b;' –

+0

@Gene Bushuyev Right , c'est exactement l'équivalent du deuxième exemple (de travail) du PO: Obtenir l'adresse du membre 'b' spécifique du parent, pas un pointeur général vers le membre. –

+2

Une référence de la norme qui parle de la syntaxe et de la différence? Comment cette syntaxe '& (B :: b)' signifie que 'b' appartient à une instance spécifique de' A'? – Nawaz

6

La différence entre les deux instructions devient plus évidente lorsque vous essayez de renvoyer la valeur:

int*  foo() { return &(B::b);} // This is a pointer to an int 


int A::* foo() { return &B::b; } // This is a pointer to a member of type int 

Qu'est-ce que vous voulez faire est l'accès via l'Objet: A partir de A, vous êtes autorisé

int A::* foo() { return &A::b; } // This is a pointer to a member of type int 

pour y accéder.
L'accès via B comme ça est l'accès de l'extérieur et déclenche ainsi les spécificateurs d'accès.

+0

Cela explique mieux la différence, à mon avis. +1 – Nawaz

+0

"L'accès via B comme ça l'accède de l'extérieur et déclenche ainsi les spécificateurs d'accès." n'est pas correct, je pense. Le deuxième exemple d'OP le prouve. Imaginons aussi que A contienne un autre attribut nommé 'b' shadowing' B :: b'. Ensuite, vous pouvez utiliser '& (A :: b)' et '& (B :: b)' pour les différencier. –

+0

@Tilman Vogel: Je ne suis pas d'accord. C'est pourquoi je montre ci-dessus comment les deux sont différenciés par ce qui est réellement généré par l'opérateur et. –

7

Ceci est juste une supplémentation.
§5.3.1/2 dit:

Le résultat de l'opérateur & unaire un pointeur vers son opérande. L'opérande doit être une lvalue ou un ID qualifié. Dans le premier cas, si le type de l'expression est « T », le type du résultat est « pointeur vers T. » ...
Pour un -id qualifié, ...Si le membre est un membre non-statique de la classe C de type T , le type du résultat est « pointeur à un membre de la classe C de type T. »

Selon §5.1/7, B::b relève du cas de l'identificateur qualifié, mais pas du (B::b). Ainsi, le compilateur l'interprète comme une valeur lvalue.

+0

+1 pour la référence. Maintenant, c'est un peu plus clair! – Nawaz

Questions connexes