2010-11-07 26 views
18

Alerta de alerta: Quizás una pregunta estúpida. :)'Base directa inaccesible' causada por herencia múltiple

#include <iostream> 

using namespace std; 

class Base 
{ 
    public: 
     virtual void YourMethod(int) const = 0; 
}; 

class Intermediate : private Base 
{ 
    public: 
     virtual void YourMethod(int i) const 
     { 
      cout << "Calling from Intermediate" << i << "\n"; 
     } 
}; 

class Derived : private Intermediate, public Base 
{ 
    public: 
     void YourMethod(int i) const 
     { 
      cout << "Calling from Derived : " << i << "\n"; 
     } 
}; 

int main() 
{ 
} 

Puede alguien explicar para mí por qué esto arroja el compilador advertencia:

main.cpp:21: warning: direct base ‘Base’ inaccessible in ‘Derived’ due to ambiguity 

Ahora, entiendo que no hay forma en que este código funcionará. Quiero saber por qué. Base es privado a Intermediate por lo que no debería ser visible para Derived a través de Intermediate. Entonces, ¿de dónde viene la ambigüedad? En constructor?

+0

No es un error del compilador su [sólo una advertencia] (http://ideone.com/Pe0nK). –

+0

Me imagino que llamar a Derived-> YourMethod (5) debería estar bien ... después de todo, tanto en Base como en Intermedio, YuorMethod es virtual, entonces ¿por qué no puedes definir tu propia implicación? ¿No estás llamando a las funciones base de ninguna manera, por lo que el asunto público privado no debería importar? – thecoshman

+0

@Prasoon: Editado. – nakiya

Respuesta

25

Esto no tiene nada que ver con las funciones de anulación. Tiene que ver con las conversiones. Realmente no tiene que ver con la accesibilidad (es decir, "privada" o tal) directamente tampoco. Aquí está un ejemplo más simple

struct A { int a; }; 
struct B : A { }; 
struct C : B, A { }; // direct A can't be referred to! 

Se puede hacer referencia a la A objeto indirecto convirtiendo en primer lugar a B y luego a A:

B *b = &somec; 
A *a = b; 

No se puede hacer por ejemplo con la directa Un objeto. Si intenta convertir directamente a A, tendrá dos posibilidades. Se deduce que es imposible hacer referencia a los miembros de datos no estáticos del objeto directo A dado un objeto Derived.

Tenga en cuenta que la accesibilidad es ortogonal a la visibilidad. Algo puede ser accesible aunque no sea visible (por ejemplo, refiriéndolo a un nombre calificado), y algo puede ser visible aunque no sea accesible. Incluso si todas las derivaciones anteriores se declararan private, el problema seguiría apareciendo: El acceso se verifica por última vez; no afectará la búsqueda de nombres ni las reglas de conversión.

Además, cualquiera puede convertir a una clase base privada no ambigua con un comportamiento definido (el estándar de C++ hace una excepción para esto) usando un molde de estilo C, aunque normalmente no se le otorgaría acceso para hacerlo. Y luego todavía hay amigos y la clase en sí que puede convertir libremente.

+0

¿Cómo hacer referencia a algo con un nombre calificado puede ser un ejemplo de algo accesible, incluso si no está visible? – nakiya

+0

@nakiya 'clase A {int a; void f() {int a;/* outer a no está visible * /}}; 'pero puede referirse a él por' A :: a'. Después del 'a' local en' f', el miembro de la clase era accesible (porque es la misma clase), pero su nombre estaba oculto por el 'a' local. –

+0

¿Su analogía mantiene los árboles de herencia? Si heredo 'B' de' A' y heredo públicamente 'C' de' B', entonces 'A' no debería estar visible para' C' (protected lo haría posible). Por lo tanto, si heredo 'C' de' A' también, 'C' idealmente solo debería ver su' A' no 'B's' A'. Pero, estoy de acuerdo, este no es el caso. – nakiya

6

La respuesta de Johannes cubre los hechos básicos. Pero hay un poco más. Por lo tanto, considere

struct Base 
{ 
    Base(int) {} 
    void foo() const {} 
}; 

struct Intermediate: Base 
{ 
    Intermediate(int x) 
     : Base(x) 
    {} 
}; 

struct Derived: Intermediate, Base 
{ 
    Derived(int x) 
     : Intermediate(x) 
     , Base(x)   // OK 
    {} 
}; 

int main() 
{ 
    Derived o(667); 
    o.foo();    // !Oops, ambiguous. 
    o.Base::foo();   // !Oops, still ambiguous. 
} 

Cuando compilo consigo, ya que por ahora (después de la respuesta Johannes') que se puede esperar,

 
C:\test> gnuc x.cpp 
x.cpp:15: warning: direct base 'Base' inaccessible in 'Derived' due to ambiguity 
x.cpp: In function 'int main()': 
x.cpp:25: error: request for member 'foo' is ambiguous 
x.cpp:4: error: candidates are: void Base::foo() const 
x.cpp:4: error:     void Base::foo() const 
x.cpp:26: error: 'Base' is an ambiguous base of 'Derived' 

C:\test> msvc x.cpp 
x.cpp 
x.cpp(15) : warning C4584: 'Derived' : base-class 'Base' is already a base-class of 'Intermediate' 
     x.cpp(2) : see declaration of 'Base' 
     x.cpp(7) : see declaration of 'Intermediate' 
x.cpp(25) : error C2385: ambiguous access of 'foo' 
     could be the 'foo' in base 'Base' 
     or could be the 'foo' in base 'Base' 
x.cpp(25) : error C3861: 'foo': identifier not found 

C:\test> _ 

Cómo resolver dependerá de si todo está bien con un solo sub -objeto de la clase Base (como es el caso cuando Base es una interfaz pura), o Intermediate realmente requiere su propio sub-objeto Base.

El último caso, dos sub-objetos Base, probablemente no es lo que quieres, pero si quieres eso, entonces una cura es introducir otra clase intermedia, por ejemplo, ResolvableBase.

térmica:

struct Base 
{ 
    Base(int) {} 
    void foo() const {} 
}; 

struct Intermediate: Base 
{ 
    Intermediate(int x) 
     : Base(x) 
    {} 
}; 

struct ResolvableBase: Base 
{ 
    ResolvableBase(int x): Base(x) {} 
}; 

struct Derived: Intermediate, ResolvableBase 
{ 
    Derived(int x) 
     : Intermediate(x) 
     , ResolvableBase(x) 
    {} 
}; 

int main() 
{ 
    Derived o(667); 
    o.ResolvableBase::foo(); // OK. 
} 

En el primer caso, donde por ejemplo,Base es una interfaz y solo se necesita un subobjeto Base, puede usar la herencia virtual.

La herencia virtual generalmente agrega un poco de sobrecarga de tiempo de ejecución, y Visual C++ no es muy aficionado a ella.

sino que le permite "Heredar" una implementación de una interfaz, como en Java y C#:

struct Base 
{ 
    Base(int) {} 
    virtual void foo() const = 0; 
}; 

struct Intermediate: virtual Base 
{ 
    Intermediate(int x) 
     : Base(x) 
    {} 
    void foo() const {}  // An implementation of Base::foo 
}; 

struct Derived: virtual Base, Intermediate 
{ 
    Derived(int x) 
     : Base(x) 
     , Intermediate(x) 
    {} 
}; 

int main() 
{ 
    Derived o(667); 
    o.foo(); // OK. 
} 

Sutileza: Me cambió el orden de la lista de herencia con el fin de evitar g ++ sillywarnings sobre el orden de inicialización.

Molesto: Visual C++ emite un tonto C4250 sobre la herencia (de la implementación) a través de la dominación. Es como "advertencia: estás usando una función principal estándar". Oh, bueno, solo apágalo.

Saludos & HTH.,

+0

Mi problema es que, en lugar de utilizar Derivado por sí solo, cuando lo utiliza a través de un identificador de tipo 'Intermedio' (en mi pregunta) no debería haber problemas de resolución porque' Intermedio' deriva en privado de 'Base'. – nakiya

+0

@nakiya: asumiendo por "un asa" te refieres a "una referencia", bueno no lo veo en tu pregunta, pero simplemente no es un problema. Sin embargo, el compilador aún puede advertir acerca de la definición 'Derived'. Pero, ¿por qué heredas directamente de 'Base' en' Derived'? Cheers, –

+0

Por "un mango" me refiero a "una referencia o un puntero". Vea esta pregunta (Y mi respuesta) como la respuesta a su segunda pregunta: http://stackoverflow.com/questions/4117538/how-to-be-sure-a-method-is-overriding-an-existing-virtual- one-in-c/4118483 # 4118483 – nakiya