c++ - que - ¿Por qué no funciona una declaración de uso para resolver el problema del diamante?
que es herencia multiple biologia (3)
Además de la respuesta de TC, me gustaría agregar que la búsqueda de nombres en la clase derivada se explica en el estándar con bastante detalle en la sección 10.2.
Aquí lo que se dice sobre el procesamiento de declaraciones de uso:
10.2 / 3: El conjunto de búsqueda (...) consta de dos conjuntos de componentes: el conjunto de declaración, un conjunto de miembros llamado f; y el conjunto de subobjetos, un conjunto de subobjetos donde se encontraron declaraciones de estos miembros (posiblemente incluyendo declaraciones de uso). En el conjunto de declaraciones, las declaraciones de uso se reemplazan por los miembros que designan, y las declaraciones de tipo (incluidos los nombres de clase inyectados) se reemplazan por los tipos que designan.
Entonces, cuando intentas declarar en la
struct C
using B1::f; // you hope to make clear that B1::f is to be used
de acuerdo con las reglas de búsqueda, su compilador encuentra los posibles candidatos:
B1::f
y
B2::f
por lo que aún es ambiguo.
Por favor considere el siguiente código:
struct A
{
void f()
{
}
};
struct B1 : A
{
};
struct B2 : A
{
};
struct C : B1, B2
{
void f() // works
{
B1::f();
}
//using B1::f; // does not work
//using B1::A::f; // does not work as well
};
int main()
{
C c;
c.f();
return 0;
}
Le pido amablemente que no copie y pegue una respuesta estándar sobre cómo resolver el problema del diamante ("usar herencia virtual"). Lo que pregunto aquí es por qué no funciona una declaración de uso en este caso. El error exacto del compilador es:
In function ''int main()'':
prog.cpp:31:6: error: ''A'' is an ambiguous base of ''C''
c.f();
Tengo la impresión de que una declaración de uso debería funcionar a partir de este ejemplo:
struct A
{
void f()
{
}
};
struct B
{
void f()
{
}
};
struct C : A, B
{
using A::f;
};
int main()
{
C c;
c.f(); // will call A::f
return 0;
}
Alguien más puede encontrar la cita estándar, pero voy a explicarle conceptualmente.
No funciona porque una declaración de uso solo afecta a la búsqueda de nombres.
Su
declaración de uso
hace que la búsqueda de nombres tenga éxito donde de otro modo fallaría, es decir, le dice al compilador
dónde encontrar la función
f
.
Pero no le dice en
qué subobjeto
A
actúa
, es decir, cuál se pasará como implícito
this
parámetro cuando se llama
f
.
Solo hay una única función
A::f
aunque haya dos subobjetos
A
de
C
, y toma implícito
this
argumento de tipo
A*
.
Para llamarlo en un objeto
C*
,
C*
debe convertirse implícitamente en
A*
.
Esto siempre es ambiguo y no se ve afectado por ninguna
declaración de uso
.
(Esto tiene más sentido si coloca miembros de datos dentro de
A
Entonces
C
tendría dos de cada miembro de datos. Cuando se llama a
f
, si accede a miembros de datos, ¿accede a los del subobjeto
A
heredado de
B1
, o el los del subobjeto
A
heredado de
B2
?)
Hay una nota en [namespace.udecl] / p17 que aborda esta situación directamente:
[ Nota : Debido a que una declaración de uso designa un miembro de clase base (y no un subobjeto de miembro o una función de miembro de un subobjeto de clase base), una declaración de uso no puede usarse para resolver ambigüedades de miembro heredadas. Por ejemplo,
struct A { int x(); }; struct B : A { }; struct C : A { using A::x; int x(int); }; struct D : B, C { using C::x; int x(double); }; int f(D* d) { return d->x(); // ambiguous: B::x or C::x }
- nota final ]