¿Cómo funciona el código siguiente 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 {};

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

  2. Nota 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 eliminan) de boost::is_base_of. Y esto funciona en un amplio rango de compiladores.

  • Es muy confuso de utilizar el mismo identificador para un parámetro de plantilla y un verdadero nombre de la clase…
  • M., he encargué para corregir 🙂
  • Did you mean en 3: ¿por Qué static yes check<int>(D*, T=int); es mejor que static no check(B*, int); ?
  • No. Por qué la función de la plantilla es mejor que la de los no-plantilla de uno. Y ¿por qué es importante que operator B*() es const?
  • Hace algún tiempo escribí una implementación alternativa de is_base_of: ideone.com/T0C1V no funciona con las anteriores versiones de GCC, aunque (GCC4.3 funciona bien).
  • se le preguntó acerca de static yes check(B*, int); pero el código sólo tiene un static no check(B*, int);.
  • su alternativa de solución en ideone.com no funciona en VC2008 y VC2010.
  • eso es probablemente porque permite la unión de la no-const referencias a rvalues. Si usted deshabilita las extensiones, entonces tal vez se trabaja allí. En cualquier caso, se debe advertir en alta advertir los niveles.
  • Ok, voy a dar un paseo.
  • el enlace a ideone.com/T0C1V da una página en blanco
  • como esta es por lo que yo siempre prefijo template argumentos con T_ 😉
  • En gcc is_base_of se deriva de __es_base_de. No puedo encontrar __es_base_de ‘s definición en cualquier lugar. ¿Alguien sabe en que el encabezado de __es_base_de se define?
  • Esta aplicación no es correcta. is_base_of<Base,Base>::value debe ser true; esto devuelve false.
  • además incorrectamente computación is_base_of<A,A>, esta aplicación también calcula de forma incorrecta is_base_of<void,A>!
  • __is_base_of(T,U) es un compilador integrado, un.k.una. magia. No es parte del lenguaje C++; es intrínseco a GCC. La única razón por la que existe (AFAIK) es acelerar la evaluación de este tipo de rasgo, que (si no utiliza la magia builtin) requiere un montón de ejecución de plantilla y la resolución de sobrecarga. Hay mucha más magia builtins, tales como __is_union, __is_trivially_constructible, y __is_final.
  • puede que esta aplicación sea corregido para corregir ese error?

5 Comentarios

  1. 107

    Si están relacionados

    Vamos 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 convertido a 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 la clase, el siguiente candidato funciones son sintetizados por primera check función de acuerdo a 13.3.1.5/1

    D* (Host<B, D>&)

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

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

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

    Esos son los dos función de conversión de los candidatos que toman 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) por 13.3.3.2/3b1sb4 y se utiliza para convertir a B* para el segundo check función.

    Si a usted le quitar 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 un 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, a continuación, el tipo de retorno de ellos decide que es mejor de acuerdo a 13.3.3/1. Por lo tanto, si usted quiere quitar la const, a continuación, la primera sería tomado, porque B* convierte mejor B* de D* a B*.

    Ahora lo definido por el usuario de la conversión de la secuencia es mejor? El de la segunda o la primera función de la comprobación? La regla es que el usuario define la conversión de secuencias sólo se pueden comparar si se usa la misma función de conversión o constructor de acuerdo a 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 que 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 (de nuevo 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!

    Aviso que ya no necesitan a en realidad convertir a una clase base, podemos entonces reconocer la herencia privada porque si se puede convertir de un D* a un B* no depende de la forma de herencia de acuerdo a 4.10/3

    Si no están relacionados

    Ahora vamos a asumir que no están relacionadas por la herencia. Así, para la primera función, tenemos los siguientes candidatos

    D* (Host<B, D>&) 

    Y para el segundo, ahora tenemos otro conjunto

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

    Ya que no se puede convertir 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, nos sería ambigua si no por el hecho de que la primera función es una plantilla. Las plantillas son de segunda elección cuando hay un no-función de la plantilla que es igual de bueno según 13.3.3/1. Por lo tanto, hemos de seleccionar la opción de la no-función de la plantilla (segundo), y reconocemos que no hay herencia entre B y D!

    • Ah! Andreas tenía el párrafo a la derecha, lástima que no se dio tal respuesta 🙂 Gracias por tu tiempo, me gustaría poder poner favorita de ella.
    • Este va a ser mi favorito respuesta alguna… una pregunta: ¿has leído todo el estándar de C++ o simplemente trabajando en el comité de C++?? Felicitaciones!
    • trabajo en el C++ comité no hace automáticamente que usted sabe cómo C++ trabajo 🙂 Así que definitivamente tienes que leer la parte de la Norma que se necesita saber los detalles, lo que he hecho. No he leído todo de él, así que definitivamente no puede ayudar con la mayor parte de la biblioteca Estándar o roscado preguntas relacionadas 🙂
    • Fantástica explicación. Pero me pregunto: ¿en qué momento el Comité acepta que ciertas tiempo de compilación cheques sería mejor implementado como palabras clave, que como inteligente, pero muy bizantina de la plantilla de la acrobacia?
    • Para ser justos, la especificación no prohibir el std:: rasgos de utilizar algún compilador de magia para la biblioteca estándar de implemers se puede usar de ellos como. Se evitará la plantilla de acrobacias que también ayuda a acelerar el tiempo de compilación y uso de memoria. Esto es cierto incluso si el interfaz parece std::is_base_of<...>. Todo bajo el capó.
    • Por supuesto, las bibliotecas generales como boost:: necesita para asegurarse de que tienen estas características intrínsecas disponible antes de usarlos. Y tengo la sensación de que hay algún tipo de «desafío aceptado» mentalidad entre ellos para implementar las cosas sin la ayuda del compilador 🙂
    • Buen punto, supongo que el compilador de las características intrínsecas más cercano a lo que yo estaba especulando, con lo mucho que el lenguaje se resiste a la adición de nuevas palabras! TMP sin duda puede ser un reto, e incluso divertido… a un punto 😀

  2. 24

    Veamos cómo funciona mirando los pasos.

    De inicio con el sizeof(check(Host<B,D>(), int())) parte. El compilador puede ver rápidamente que este check(...) es una llamada a una función de expresión, por lo que necesita para hacer la resolución de sobrecarga en check. Hay dos candidatos sobrecargas disponible, template <typename T> yes check(D*, T); y no check(B*, int);. Si el primero es elegido, se obtiene sizeof(yes), más sizeof(no)

    Siguiente, echemos un vistazo a la resolución de sobrecarga. La primera sobrecarga es una plantilla de creación de instancias de 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 de uno. Veremos más adelante por qué la parte de la plantilla es relevante.

    Ahora mira en la conversión de secuencias que son necesarios. Para la primera de sobrecarga, hemos 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 posiblemente hay 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 relacionadas por la herencia de la conversión de la secuencia de Host<B,D>::operator D*() + D*->B* existen. Ahora suponga que D hecho hereda de B. La conversión de dos secuencias son Host<B,D> -> Host<B,D> const -> operator B* const -> B* y Host<B,D> -> operator D* -> D* -> B*.

    Así, para B y D, no check(<Host<B,D>(), int()) sería ambigua. Como resultado, 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-plantilla de funciones, es decir,no check(B*, int).

    Usted ahora 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 resolución de sobrecarga antes de la comprobació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 para el Host<B,D> -> Host<B,D> const paso, no hay ambigüedad, y no check(B*, int) siempre sería elegido.

    • Su explicación no tiene en cuenta la presencia de const. Si su respuesta es verdadera, entonces no const es necesario. Pero no es cierto. Quitar const y el truco no funcionará.
    • Sin la constante de la conversión de dos secuencias para no check(B*, int) no son más ambiguos.
    • Si deja sólo no check(B*, int), a continuación, relacionados B y D, no sería ambigua. El compilador sería inequívocamente elegir operator D*() para realizar la conversión, porque no tiene un const. Se trata más bien de un bit en la dirección opuesta: Si eliminar la const, introducir un poco de sentido de la ambigüedad, pero que se resuelve por el hecho de que operator B*() proporciona un rendimiento superior de tipo que no necesita un puntero de conversión a B* como D* hace.
    • De hecho, es el punto: la ambigüedad es entre los dos tipos de conversión de secuencias para obtener una B* de la <Host<B,D>() temporal.
    • Esta es una respuesta mejor. Gracias! Así, según tengo entendido, como si una función es mejor, pero ambiguo, a continuación, otra función es el elegido?
    • De hecho. Pero tenga en cuenta que esto no es una regla general. Con las plantillas, la Sustitución de Error No Es Un Error (SFINAE). Principios en el diseño de C++, se observó que no intencionadas de la plantilla de casos a menudo sería errores, que es la razón por la SFINAE se introdujo la regla. Esta regla es ahora a menudo se utiliza intencionalmente en situaciones como esta.

  3. 4

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

    Usted puede verificar 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 una base privado no impide la verificación de que está teniendo lugar, lo único que haría sería evitar la conversión, pero no pedimos la conversión real 😉

    • Tipo de. La base No se realiza la conversión a todos. host es arbitrariamente convertido a D* o B* en la expresión no evaluada. Por alguna razón, D* es preferible a B* bajo ciertas condiciones.
    • Creo que la respuesta está en 13.3.1.1.2 pero aún tengo que ordenar los detalles 🙂
    • Mi respuesta sólo explica el «por qué incluso obras privadas» parte, sellibitze la respuesta ciertamente es más completa, aunque yo estoy esperando una explicación clara de la resolución completa del proceso, dependiendo de los casos.
  4. 2

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

    Los detalles exactos son bastante complicadas. Usted tiene que averiguar la precedences de diversos 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 se consideran igual de bueno, no-las plantillas son elegidos sobre la función de las plantillas.

    Nunca he necesitado a buscar cómo estas reglas interactuar. Pero parece pedido parcial está dominando el otro sobrecarga de las reglas de resolución. Cuando D no se derivan de B el orden parcial de las 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 pide una conversión de D* a B* que requeriría al público inheritence

    • Creo que es algo así como que, yo recuerdo haber visto una discusión extensa sobre el impulso de archivos acerca de la aplicación de is_base_of y los bucles de los contribuyentes tuvieron que pasar para asegurar esto.
    • The exact details are rather complicated – ese es el punto. Por favor, explique. Yo quiero saber.
    • Bien, pensé que me he apuntado en la dirección correcta. Echa un vistazo a cómo los diferentes sobrecarga de las reglas de resolución de interactuar en este caso. La única diferencia entre D la derivada de B y D no derivadas de B con respecto a la resolución de este caso de sobrecarga es el orden parcial de la regla. Resolución de sobrecarga se describe en el §13 de la estándar de C++. Usted puede obtener un borrador para libre: open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1804.pdf
    • Resolución de sobrecarga se extiende por 16 páginas en borrador. Supongo que, si usted realmente necesita para entender las reglas y la interacción entre ellos para este caso usted debe leer la sección completa §13.3. No cuento en llegar aquí una respuesta que es 100% correcto y hasta sus estándares.
    • por favor vea mi respuesta para una explicación de si usted está interesado.
  5. 0

    Siguiente 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, de ahí que uno de los operadores de conversión debe ser const.

Dejar respuesta

Please enter your comment!
Please enter your name here