Frage

habe ich ein Problem in Bezug auf Mitglied Zeiger. Der folgende Code schlägt fehl sowohl Oracle Solaris Studio 12.2 CC zu kompilieren mit und Cygwin GCC 4.3.4, sondern arbeitet mit 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;
}

Auf dem vorletzten Zeile beiden Compiler oben erwähnten nicht ein Spiel für Bar<B>::foo(int A::*) zu finden. Ich schrieb einen einfachen Test, um zu bestätigen, dass die Art des Ausdrucks &B::x ist eigentlich 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;
}

Die folgende Problemumgehung funktioniert mit GCC (nicht mit Oracle CC noch nicht getestet), aber nicht mit VC ++ aufgrund Mehrdeutigkeit:

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));
  }
};

Meine Frage: Welches Verhalten ist richtig? Offenbar VC ++ tut eine implizite upcast von int A::* zu int B::* den Aufruf der Elementfunktion Vorlage gerecht zu werden, sollten nicht die beiden anderen Compiler die gleiche denken Sie daran tun?

War es hilfreich?

Lösung

Eine Umwandlung von int A::* zu int B::* erlaubt ist, und das ist nicht das Problem. Das Problem ist in den Schablonen Argument Abzug, wie Sie sehen können, wenn Sie das folgende Programm versuchen, die ein Template-Argument <int> für B::foo und compiliert liefert, und eine Nicht-Mitglied-Funktion foo2, die die gleichen Fehler erzeugt wie B::foo zuvor.

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;
}

Ich denke, diese Situation nicht durch die Fälle abgedeckt wird, wo der Compiler soll das Template-Argument <int> auf eigene abzuleiten. 14.8.2.1p3:

  

Im Allgemeinen wird der Abbuchungsvorgang versucht Template-Argument Werte zu finden, die die abgeleitete A identisch mit A machen werden (nach dem Typ ist A wie beschrieben transformiert oben). Allerdings gibt es drei Fälle, die einen Unterschied erlauben:

  • Wenn das Original P einen Referenztyp, der deduzierten A kann (das heißt, der Typ mit dem Bezug bezeichnet) seine cv-qualifiziert als A.
  • A kann ein anderer Pointer oder Zeiger auf Elementtyp sein, der dem deduzierten A über eine Qualifizierungs Umwandlung (conv.qual) umgerechnet werden können.
  • Wenn P ist eine Klasse, und P den Formularvorlage-ID, dann kann A eine abgeleitete Klasse des abgeleiteten A. Ebenso, wenn P ein Zeiger auf eine Klasse der Formularvorlage-ID ist, kann A sein ein Zeiger auf eine abgeleitete Klasse zeigte die abgeleitete A.

Hier "P" ist das Argument Typ der Template-Funktion: M B::*p, wo Vorlagentyp Parameter M bestimmt werden soll. „A“ ist die Art des tatsächlichen Arguments: int A::*. P und A ist sicherlich keinen Verweis oder eine Klasse, und die Art von Zeiger-to-Mitglied Umwandlung wir für diese Arbeit benötigen würden, ist keine Qualifikation Umwandlung (die nur const / volatile Manipulationen wie X* zu const X* oder int X::* zu const int X::* beschreibt ).

Also das Template-Argument kann nicht abgeleitet werden, und Sie sollten die <int> expliziten Template-Parameter, um Ihren Code hinzuzufügen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top