uma pergunta sobre a precedência em C++, os operadores "endereço de" e "de resolução de escopo"
-
14-11-2019 - |
Pergunta
Olá eu tenho esse código com um compilador de erro (erro do Microsoft Visual Studio 2008):
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'
};
enquanto esse código é livre de erro:
class B
{
protected:
int b;
};
class A : public B
{
public:
void foo(){ &(B::b); }
};
Os dois trechos me parecem equivalentes, com base no meu conhecimento de precedência de operadores, porque ::tem uma precedência superior & (ver, por exemplo, a tabela 2 na página 137 de "JOINT STRIKE FIGHTER VEÍCULO AÉREO C++ PADRÕES de CODIFICAÇÃO PARA O SISTEMA de DESENVOLVIMENTO E PROGRAMA de DEMONSTRAÇÃO" http://www2.research.att.com/~bs/JSF-AV-regras.pdf )
Mas eles são diferentes...Eu acho que é algo relacionado com a "ponteiro-para-dados-membro", mas eu não sei como ele se enquadra com os operadores precedência.
Alguma explicação?
Obrigado, Alessandro
Solução
No primeiro caso, você está tomando o endereço do ponteiro-para-membro B::b
.Uma vez que tal um ponteiro NÃO é um membro do principal da A
mas um objeto separado, ele não pode acessá-lo através do mecanismo protegido.
No SEGUNDO caso, onde ele funciona, você está pedindo para o endereço do instância específica de b
, qualificando-o com a sua base de classe para que, no caso de herança múltipla para o compilador saber que a base de classe média.Neste contexto, o atributo protegido é visível.
Note que este compila:
class B
{
protected:
int b;
};
class A : public B
{
public:
void foo(){ &A::b; } // Note here &A:: instead of &B::
};
Como um exemplo, ele não funcionar pela mesma razão que o seguinte (espero que mais familiar), de código não funciona:
class B
{
protected:
int b;
};
class A : public B
{
public:
void foo(const B* b_obj) { b_obj->b; }
};
Outras dicas
Este é apenas um suplemento.
§5.3.1/2 diz:
O resultado da unário & operador um ponteiro para seu operando.O operando deve ser um lvalue ou qualificada-id.No primeiro caso, se o tipo de a expressão é "T", o tipo da o resultado é "ponteiro para T." ...
Para um qualificado-id, ...Se o membro for um não-membro estático da classe C, do tipo T, o tipo do resultado é "ponteiro para o membro da classe C, do tipo T."
De acordo com o parágrafo 5.1/7, B::b
vem sob o qualificado-identificação do caso, mas (B::b)
não.Assim, o compilador interpreta como um lvalue.
O differece entre as duas declarações se torna mais evidente quando você tentar retornar o valor:
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
O que você quer fazer é aceder a ele através de Um objeto:
int A::* foo() { return &A::b; } // This is a pointer to a member of type int
Como de Um você tem permissão para acessá-lo.
Acessando-o através de B, como a que está a aceder à mesma a partir de fora e, assim, desencadeia o acesso especificadores.