Pregunta

Tengo un problema con respecto a los punteros miembros. El siguiente código falla al compilar usando CC y ambos de Oracle Solaris Studio 12.2 cygwin GCC 4.3.4, pero funciona con Microsoft Visual C ++ 2010:

struct A {
  int x;
};

struct B : public A {
};

template<typename T> class Bar {
public:
  template<typename M> void foo(M T::*p);
};

int main(int, char *[]) {
    Bar<B> bbar;
    bbar.foo(&B::x);
    return 0;
}

En la penúltima línea de los compiladores mencionados anteriormente no encuentran a la altura de Bar<B>::foo(int A::*). Escribí una simple prueba para confirmar que el tipo de la expresión es en realidad &B::x int A::*:

// ...

static void foo(int A::*p) {
  std::cout << "A" << std::endl;
}

static void foo(int B::*p) {
  std::cout << "B" << std::endl;
}

int main(int, char *[]) {
    foo(&B::x);  // prints "A", even on MS VC++ 2010 
    return 0;
}

La siguiente solución funciona con GCC (no probado con Oracle todavía CC), pero falla con VC ++ debido a la ambigüedad:

template<typename T> class Bar {
public:
  template<typename M> void foo(M T::*p);
  template<typename M, typename _T_base> inline void foo(M _T_base::*p) {
      foo(static_cast<M T::*>(p));
  }
};

Mi pregunta: ¿Qué comportamiento es correcto? Al parecer VC ++ hace una conversión hacia arriba implícita de int A::* a int B::* para satisfacer la llamada a la función miembro de plantilla, no deben los otros dos compiladores considerar hacer lo mismo?

¿Fue útil?

Solución

Se permite una conversión de int A::* a int B::*, y ese no es el problema. El problema es que en la deducción argumento de plantilla, como se puede ver si se intenta el siguiente programa que suministra un <int> argumento de plantilla para B::foo y compila, y una foo2 función no miembro que produce el mismo error que B::foo hizo antes.

struct A {
  int x;
};

struct B : public A {
};

template <typename T> class Bar {
public:
  template<typename M> void foo(M T::*p);
};

template<typename M> void foo2(M B::*p);

int main(int, char*[]) {
  Bar<B> bbar;
  bbar.foo<int>(&B::x);
  foo2(&B::x); // error, but foo2<int>(&B::x) would work.
  return 0;
}

Creo que esta situación no es cubierto por los casos en los que se supone que el compilador para deducir el argumento <int> plantilla por su cuenta. 14.8.2.1p3:

  

En general, los intentos de proceso deducción para encontrar valores de los argumentos de plantilla que harán que el A deducida idéntica a A (después de la de tipo A se transforma como se describe anteriormente). Sin embargo, hay tres casos que permiten una diferencia:

  • Si el P original es un tipo de referencia, el A deducida (es decir, del tipo mencionado por la referencia) puede ser más cv-calificado que A.
  • A puede ser otro puntero o puntero a tipo de miembro que se puede convertir a la A deducida a través de una conversión de calificación (conv.qual).
  • Si P es una clase, y P tiene la forma de la plantilla-id, entonces A puede ser una clase derivada de la deducida A. Del mismo modo, si P es un puntero a una clase de la forma de la plantilla-id, A puede un puntero a una clase derivada apuntado por el a deducida.

Aquí "P" es de tipo argumento de la función de plantilla: M B::*p, donde parámetro de tipo de plantilla M se va a determinar. "A" es el tipo de argumento real: int A::*. P y A son ciertamente no es una referencia o una clase, y el tipo de conversión de puntero a miembro necesitaríamos para que esto funcione, no es una conversión de calificación (que describe solamente const / manipulaciones volátiles como X* a const X* o int X::* a const int X::* ).

Así que el argumento de plantilla no se puede deducir, y se debe añadir el parámetro de plantilla explícita <int> a su código.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top