C ++: razonamiento detrás de la regla de ocultación

¿Cuál es el razonamiento detrás de la regla de ocultación en C ++?

class A { void f(int); } class B : public A { void f(double); } // B::f(int) is hidden 
  • Si es una característica significativa, creo que también debería ser posible ocultar funciones sin definir nuevas funciones con el mismo nombre: algo como esto:

     class B : public A { hide void f(double); } 

    Pero esto no es posible.

  • No creo que simplifique el trabajo de los comstackdores, ya que los comstackdores deben poder mostrar las funciones cuando uses explícitamente la directiva using :

     class B : public A { using A::f; void f(double); } // B::f(int) NOT hidden 

Entonces, ¿cómo es que hay una regla de ocultamiento?


Hum, las tres respuestas parecen ser buenas y muestran diferentes razones para la regla de ocultación. No estoy seguro de qué respuesta debería aceptar.

Es una pregunta peliaguda, pero aparentemente la idea es que esta característica de ocultación ayuda a evitar errores sutiles cuando se realizan cambios en una clase base (que de otra manera podría “robar” llamadas que antes habrían sido manejadas por la clase derivada). Todavía un cambio en una clase base puede influir en el resultado de la comstackción de clases derivadas, así que no creo que entiendo al 100% esta explicación.

Estoy de acuerdo en que este tema se discute con tanta frecuencia que probablemente el ocultamiento en realidad aumenta la cantidad de “sorpresas” en los progtwigdores de C ++.

Aquí puede encontrar una discusión detallada sobre este tema …

No sé cuál es el fundamento original, pero como esconder o no esconder se trata de elecciones igualmente malas wrt. a las funciones, supongo que la razón es tener reglas uniformes : lo mismo que para los nombres definidos en los scopes de llaves rizadas anidadas.

el escondite te ayuda de alguna manera.

agregar un método a una clase base de forma predeterminada no afectará la resolución de sobrecarga para una clase derivada.

y no se topa con la resolución de sobrecarga por un error al dirigir su llamada con un argumento false , a un método de clase base con un argumento formal void* . tales cosas.

vítores y hth.,

Estoy seguro de que he visto este caso ofrecido por un Bigwig C ++, no estoy seguro de cuál:

 struct Base { void f(const Base&); }; struct Derived : Base { using Base::f; void f(double); }; int main() { Derived d; df('a'); // calls Derived::f } 

Ahora, agregue void f(int); a Base , y el significado de los cambios principales: llama a Base::f porque int es una mejor coincidencia para char : es una promoción de enteros en lugar de una conversión estándar.

No está claro si el progtwigdor realmente intentará cambiar a la base para captar llamadas con char , por lo que requerir el using para ser explícito significa que el comportamiento predeterminado es que el cambio no afecta el código de llamada. Creo que es una llamada marginal, pero creo que el comité decidió que las clases base en C ++ eran lo suficientemente frágiles como son, sin esto también 🙂

No hay necesidad de una palabra clave “ocultar” porque no existe un caso comparable para ocultar “f” de la Base cuando no está sobrecargado en Derivado.

Por cierto, he elegido los tipos, y char es deliberadamente incongruente. Puede obtener casos más sutiles con int vs unsigned int lugar de int vs char .

Otra razón para ocultar la función de miembro de la clase base (con el mismo nombre pero diferentes firmas) puede deberse a la ambigüedad causada por los parámetros opcionales. Considere el siguiente ejemplo:

 #include  class A { public: int foo(int a, int b=0) { printf("in A : %d, %d\n", a, b); } }; class B : public A { public: int foo(int a) { printf("in B : %d\n", a); foo(a); //B:foo(a) will be called unless we explicitly call A:foo(a) foo(a, 1); // compile error: no matching function for call to B:foo(int&, int) } }; int main() { B b; b.foo(10); return 0; } 

Si el método foo en la clase base no se hubiera ocultado, el comstackdor no podría decidir si se debe llamar a A::foo o a B::foo ya que la siguiente línea coincide con ambas firmas:

 foo(a); 

Probablemente, el motivo sea la especialización de plantilla. Te doy un ejemplo:

 template  struct A { void f() }; template <> struct A<1> { void f(int) }; template  struct B: A { void g() { this->f(); } }; 

La clase de plantilla B tiene un método f() , pero hasta que no cree una instancia de la clase B, no conoce la firma. Entonces la llamada this->f() es en cualquier momento “legal” . Tanto GCC como CLang no informan el error hasta que crea la instancia. Pero cuando llamas al método g() en una instancia B<1> , indican el error. Entonces, la regla de ocultamiento es más simple para verificar si su código es válido.

Informo la última parte del código utilizado en mi ejemplo.

 int main (int argc, char const *argv[]) { B<0> b0; /* valid */ B<1> b1; /* valid */ b0.g(); /* valid */ b1.g(); /* error: no matching function for call to 'B<1>::f()' */ return 0; }