88 votos

¿Cómo se `is_base_of trabajo?

¿Cómo funciona el siguiente código de trabajo?

typedef char (&yes)[1];
typedef char (&no)[2];

template <typename B, typename D>
struct Host
{
  operator B*() const;
  operator D*();
};

template <typename B, typename D>
struct is_base_of
{
  template <typename T> 
  static yes check(D*, T);
  static no check(B*, int);

  static const bool value = sizeof(check(Host<B,D>(), int())) == sizeof(yes);
};

//Test sample
class Base {};
class Derived : private Base {};

//Exspression is true.
int test[is_base_of<Base,Derived>::value && !is_base_of<Derived,Base>::value];
  1. Tenga en cuenta que B es privada de la base. ¿Cómo funciona esto?

  2. Tenga en cuenta que operator B*() es const. ¿Por qué es importante?

  3. ¿Por qué es template<typename T> static yes check(D*, T); mejor que static yes check(B*, int); ?

Nota: es la versión reducida (macros se quitan) boost::is_base_of. Y esto funciona en un amplio rango de compiladores.

88voto

Johannes Schaub - litb Puntos 256113

Si están relacionados con el

Supongamos por un momento supongamos que B es en realidad una base de D. Luego de la llamada a check, ambas versiones son viables porque Host puede ser convertida D* y B*. Es definido por el usuario, conversión de la secuencia descrita por 13.3.3.1.2 de Host<B, D> a D* y B* respectivamente. Para encontrar las funciones de conversión que puede convertir a la clase, el siguiente candidato funciones son sintetizados por primera check función 13.3.1.5/1

D* (Host<B, D>&)

La primera función de conversión no es un candidato, porque B* no se puede convertir a D*.

Para la segunda función, con los siguientes candidatos existen:

B* (Host<B, D> const&)
D* (Host<B, D>&)

Esos son los dos la función de conversión de los candidatos que tome el objeto host. La primera toma por referencia constante, y el segundo no. Así, la segunda es un partido mejor para el no-const *this objeto (el objeto implícito argumento) 13.3.3.2/3b1sb4 y se utiliza para convertir a B* para el segundo check función.

Si a usted le quite la const, tendríamos los siguientes candidatos

B* (Host<B, D>&)
D* (Host<B, D>&)

Esto significaría que no podemos seleccionar por constness más. En una ordinario de resolución de sobrecarga escenario, la llamada ahora sería ambiguo porque normalmente el tipo de retorno no participar en la resolución de sobrecarga. Para las funciones de conversión, sin embargo, hay una puerta trasera. Si dos funciones de conversión son igual de buenas, entonces el tipo de retorno de ellos decide que es mejor de acuerdo a 13.3.3/1. Por lo tanto, si usted quiere eliminar la const, la primera sería tomado, porque B* convierte mejor B* de D* a B*.

Ahora, de lo que definido por el usuario de la conversión de la secuencia es la mejor? El de la segunda o la primera función de verificación? La regla es que el usuario define la conversión de secuencias sólo se pueden comparar si utilizan la misma función de conversión o constructor según 13.3.3.2/3b2. Este es exactamente el caso aquí: Tanto el uso de la segunda función de conversión. Aviso que por lo tanto la const es importante porque obliga al compilador a tomar la segunda a la función de conversión.

Ya podemos comparar cual es mejor? La regla es que la mejor forma de conversión del tipo de retorno de la función de conversión del tipo de destino gana (otra vez por 13.3.3.2/3b2). En este caso, D* convierte mejor D* que B*. Así, la primera función está activada y reconocemos la herencia!

Observe que, dado que nunca se necesitan para realmente convertir a una clase base, podemos entonces reconocer la herencia privada, porque si se puede convertir de un D* a B* no depende de la forma de herencia según 4.10/3

Si no están relacionados

Ahora vamos a asumir que no están relacionadas mediante la herencia. Por tanto, para la primera función, tenemos los siguientes candidatos

D* (Host<B, D>&) 

Y para el segundo ahora tenemos otro juego

B* (Host<B, D> const&)

Ya que no podemos convertir a D* a B* si no tenemos una relación de herencia, que ahora no tienen en común la función de conversión entre los dos definido por el usuario de conversión de secuencias! Por lo tanto, hemos de ser ambiguo si no por el hecho de que la primera función es la de una plantilla. Las plantillas son de segunda elección cuando hay un no-plantilla de función que es igual de bueno según 13.3.3/1. Por lo tanto, seleccionamos la no-función de la plantilla (segundo), y reconocemos que no hay herencia entre B y D!

16voto

MSalters Puntos 74024

Veamos cómo funciona al mirar los pasos.

Comience con el sizeof(check(Host<B,D>(), int())) parte. Hay dos candidatos sobrecargas disponible, template <typename T> yes check(D*, T); y no check(B*, int);. Si el primero es elegido, obtendrá sizeof(yes), más sizeof(no)

A continuación, vamos a mirar a la resolución de sobrecarga. La primera sobrecarga es una creación de instancias de plantilla check<int> (D*, T=int) y el segundo candidato es check(B*, int). Los argumentos siempre son Host<B,D> y int(). El segundo parámetro claramente no distingue de ellos, tan sólo sirvió para hacer la primera sobrecarga de una plantilla. Veremos más adelante por qué la parte de la plantilla es relevante.

Ahora mira a la conversión de secuencias que son necesarios. Para la primera de sobrecarga, tenemos Host<B,D>::operator D* - una conversión definida por el usuario. Para el segundo, la sobrecarga es más complicado. Necesitamos un B*, pero hay posiblemente dos de conversión de secuencias. Uno es a través de Host<B,D>::operator B*() const. Si (y sólo si) B y D están relacionados por la herencia de la conversión de la secuencia Host<B,D>::operator D*() + D*->B* existir. Ahora, supongamos D de hecho hereda de B. Las dos de la conversión de secuencias Host<B,D> -> Host<B,D> const -> operator B* const -> B* y Host<B,D> -> operator D* -> D* -> B*.

Así, para los relacionados con la B y la D, no check(<Host<B,D>(), int()) sería ambiguo. Como resultado de ello, la plantilla yes check<int>(D*, int) es elegido. Sin embargo, si D no se hereda de B, entonces, no check(<Host<B,D>(), int()) no es ambiguo. En este punto, la resolución de sobrecarga no puede suceder baed en menor conversión de la secuencia. Sin embargo, dada la igualdad de la conversión de secuencias, la resolución de sobrecarga prefiere no de la plantilla de funciones, es decir, no check(B*, int).

Ahora puede ver por qué no importa que la herencia es privado: que la relación sólo sirve para eliminar no check(Host<B,D>(), int()) de la sobrecarga de resolución antes de la verificación de acceso sucede. Y también se puede ver por qué la operator B* const debe ser const; de otra manera, no hay necesidad de que el Host<B,D> -> Host<B,D> const paso, no hay ambigüedad, y no check(B*, int) siempre sería elegido.

3voto

Matthieu M. Puntos 101624

La private poco es completamente ignorado por is_base_of debido a la resolución de sobrecarga se produce antes de las comprobaciones de accesibilidad.

Usted puede comprobar esto simplemente:

class Foo
{
public:
  void bar(int);
private:
  void bar(double);
};

int main(int argc, char* argv[])
{
  Foo foo;
  double d = 0.3;
  foo.bar(d);       // Compiler error, cannot access private member function
}

Lo mismo se aplica aquí, el hecho de que B es privada, de base no impide la verificación de que está teniendo lugar, lo único que haría sería evitar la conversión, pero nunca nos pide la conversión real ;)

2voto

sellibitze Puntos 13607

Posiblemente tiene algo que ver con el orden parcial w.r.t. resolución de sobrecarga. D* es más especializado que B* en caso D deriva de B.

Los detalles exactos son bastante complicadas. Usted tiene que averiguar la precedences de varios sobrecarga de las reglas de resolución. Orden parcial es uno. Longitudes y tipos de conversión de las secuencias es otro. Finalmente, si dos viable funciones que son consideradas igual de bueno, no plantillas se eligió en función de las plantillas.

Nunca he necesitado buscar cómo estas reglas interactuar. Pero parece pedido parcial domina a la otra la resolución de sobrecarga reglas. Cuando D no se derivan de B el orden parcial reglas no se aplican y la no-plantilla es más atractivo. Cuando D se deriva de B, orden parcial se activa y hace que la plantilla de función más atractivo-como parece.

Como para la herencia de ser privete: el código nunca se pide una conversión de D* B* que requeriría al público inheritence

0voto

Hertz Puntos 21

Siguientes sobre su segunda pregunta, tenga en cuenta que si no fuera por const, Host sería mal formado si instanciado con B == D. Pero is_base_of está diseñado de tal manera que cada clase es una base de sí mismo, por lo tanto, uno de los operadores de conversión debe ser const.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X