Considere este código:
struct A
{
void foo() const
{
std::cout << "const" << std::endl;
}
private:
void foo()
{
std::cout << "non - const" << std::endl;
}
};
int main()
{
A a;
a.foo();
}
El error del compilador es:
error: 'void A :: foo ()' es privado`.
Pero cuando elimino el privado, simplemente funciona. ¿Por qué no se llama al método público const cuando el que no es const es privado?
En otras palabras, ¿por qué la resolución de sobrecarga viene antes que el control de acceso? Esto es extraño. ¿Crees que es consistente? Mi código funciona y luego agrego un método, y mi código de trabajo no se compila en absoluto.
Respuestas:
Cuando llama
a.foo();
, el compilador pasa por la resolución de sobrecarga para encontrar la mejor función para usar. Cuando construye el conjunto de sobrecarga, encuentray
Ahora, dado
a
que no lo esconst
, la versión no constante es la mejor coincidencia, por lo que el compilador eligevoid foo()
. Luego, se establecen las restricciones de acceso y se obtiene un error del compilador, ya quevoid foo()
es privado.Recuerde, en la resolución de sobrecarga no es "encontrar la mejor función utilizable". Es "encontrar la mejor función e intentar usarla". Si no puede debido a restricciones de acceso o si se está eliminando, aparece un error del compilador.
Bueno, veamos:
Ahora digamos que en realidad no quise hacerlo
void foo(Derived * d)
privado. Si el control de acceso fuera lo primero, este programa se compilaría, se ejecutaría yBase
se imprimiría. Esto podría ser muy difícil de rastrear en una base de código grande. Dado que el control de acceso viene después de la resolución de sobrecarga, obtengo un buen error del compilador que me dice que no se puede llamar a la función que quiero que llame, y puedo encontrar el error mucho más fácilmente.fuente
En última instancia, esto se reduce a la afirmación del estándar de que la accesibilidad no debe tenerse en cuenta al realizar la resolución de sobrecargas . Esta afirmación se puede encontrar en [over.match] cláusula 3:
y también la Nota en la cláusula 1 de la misma sección:
En cuanto a por qué, puedo pensar en un par de posibles motivaciones:
fuente
Suponga que el control de acceso vino antes de la resolución de sobrecarga. Efectivamente, esto significaría que la
public/protected/private
visibilidad controlada en lugar de la accesibilidad.La sección 2.10 de Diseño y evolución de C ++ de Stroustrup tiene un pasaje sobre esto donde analiza el siguiente ejemplo
Stroustrup menciona que un beneficio de las reglas actuales (visibilidad antes que accesibilidad) es que (temporalmente) cambiar el
private
interiorclass X
enpublic
(por ejemplo, para los propósitos de depuración) es que no hay un cambio tranquilo en el sentido del programa anterior (es decir,X::a
se intenta ser accedido en ambos casos, lo que da un error de acceso en el ejemplo anterior). Sipublic/protected/private
controlara la visibilidad, el significado del programa cambiaría (globala
se llamaría conprivate
, de lo contrarioX::a
).Luego afirma que no recuerda si fue por diseño explícito o un efecto secundario de la tecnología de preprocesador utilizada para implementar el predecesor de C con Classess al C ++ estándar.
¿Cómo se relaciona esto con su ejemplo? Básicamente porque la resolución de sobrecarga estándar se ajusta a la regla general de que la búsqueda de nombres viene antes que el control de acceso.
fuente
Dado que el
this
puntero implícito es non-const
, el compilador primero comprobará la presencia de una noconst
versión de la función antes de unaconst
versión.Si marca explícitamente el que no es
const
unoprivate
, la resolución fallará y el compilador no continuará buscando.fuente
Es importante tener en cuenta el orden de las cosas que suceden, que es:
delete
d), falle.(3) ocurre después de (2). Lo cual es realmente importante, porque de lo contrario hacer funciones
delete
doprivate
se volvería algo sin sentido y mucho más difícil de razonar.En este caso:
A::foo()
yA::foo() const
.A::foo()
porque la última implica una conversión de calificación en el implícitothis
argumento .A::foo()
esprivate
y no tiene acceso a él, por lo tanto, el código está mal formado.fuente
Esto se reduce a una decisión de diseño bastante básica en C ++.
Al buscar la función para satisfacer una llamada, el compilador realiza una búsqueda como esta:
Busca hasta encontrar el primer 1 ámbito en el que hay algo con ese nombre.
El compilador encuentra todas las funciones (o functores, etc.) con ese nombre en ese ámbito.
Luego, el compilador sobrecarga la resolución para encontrar el mejor candidato entre los que encontró (sean accesibles o no).
Finalmente, el compilador comprueba si la función elegida es accesible.
Debido a ese orden, sí, es posible que el compilador elija una sobrecarga que no es accesible, aunque hay otra sobrecarga que es accesible (pero no elegida durante la resolución de sobrecarga).
En cuanto a si sería posible hacer las cosas de otra manera: sí, sin duda es posible. Sin embargo, definitivamente conduciría a un lenguaje bastante diferente al de C ++. Resulta que muchas decisiones aparentemente menores pueden tener ramificaciones que afectan mucho más de lo que podría ser inicialmente obvio.
fuente
Los controles de acceso (
public
,protected
,private
) no afectan a la sobrecarga de resolución. El compilador eligevoid foo()
porque es la mejor combinación. El hecho de que no sea accesible no cambia eso. Quitarlo solo dejavoid foo() const
, que es entonces la mejor (es decir, la única) coincidencia.fuente
En esta llamada:
Siempre hay un
this
puntero implícito disponible en cada función miembro. Y laconst
calificación dethis
se toma de la referencia / objeto de llamada. El compilador trata la llamada anterior como:Pero tiene dos declaraciones de las
A::foo
cuales se trata como :Por resolución de sobrecarga, el primero se seleccionará para no constante
this
, el segundo se seleccionará para aconst this
. Si quita el primero, el segundo se unirá a ambosconst
ynon-const
this
.Después de la resolución de sobrecarga para seleccionar la mejor función viable, viene el control de acceso. Dado que especificó el acceso a la sobrecarga elegida como
private
, el compilador se quejará.El estándar lo dice:
Pero si haces esto:
Entonces, solo
const
se ajustará la sobrecarga.fuente
La razón técnica ha sido respondida por otras respuestas. Solo me enfocaré en esta pregunta:
Así se diseñó el lenguaje. La intención es intentar llamar a la mejor sobrecarga viable, en la medida de lo posible. Si falla, se activará un error para recordarle que considere el diseño nuevamente.
Por otro lado, suponga que su código se compiló y funcionó bien con la
const
función miembro que se invoca. Algún día, alguien (tal vez usted mismo) decide cambiar la accesibilidad de laconst
función de no miembro deprivate
apublic
. Entonces, el comportamiento cambiaría sin errores de compilación. Sería una sorpresa .fuente
Porque la variable
a
de lamain
función no se declara comoconst
.Las funciones miembro constantes se llaman en objetos constantes.
fuente
Los especificadores de acceso no afectan la búsqueda de nombres y la resolución de llamadas a funciones, nunca. La función se selecciona antes de que el compilador compruebe si la llamada debe desencadenar una infracción de acceso.
De esta forma, si cambia un especificador de acceso, se le avisará en tiempo de compilación si hay una infracción en el código existente; si se tuviera en cuenta la privacidad para la resolución de llamadas a funciones, el comportamiento de su programa podría cambiar silenciosamente.
fuente