Question

J'ai un problème en ce qui concerne les pointeurs membres. Le code suivant ne peut pas compiler à l'aide à la fois de Oracle Solaris Studio de 12.2 CC et de Cygwin GCC 4.3.4, mais fonctionne avec 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;
}

A la dernière ligne suivante les deux compilateurs mentionnés ci-dessus ne parviennent pas à trouver une correspondance pour Bar<B>::foo(int A::*). J'ai écrit un simple test pour confirmer que le type de l'expression &B::x est en fait 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 solution suivante fonctionne avec GCC (non testé avec Oracle CC encore) mais échoue avec VC ++ en raison de l'ambiguïté:

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

Ma question: Quel comportement est correct? Apparemment, VC ++ fait un upcast implicite de int A::* à int B::* pour satisfaire l'appel au modèle de fonction membre, ne devrait pas les deux autres compilateurs considèrent faire la même chose?

Était-ce utile?

La solution

Une conversion de int A::* à int B::* est autorisé, et ce n'est pas le problème. Le problème est en déduction de l'argument de modèle, comme vous pouvez le voir si vous essayez le programme suivant qui fournit un argument de modèle <int> pour B::foo et compiles, et une fonction non-membre foo2 qui produit la même erreur que B::foo a fait avant.

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

Je pense que cette situation ne sont pas couverts par les cas où le compilateur est censé pour en déduire l'argument modèle <int> lui-même. 14.8.2.1p3:

  

En général, les tentatives de processus de déduction pour trouver des valeurs d'argument de modèle qui feront l'identique à A déduite A (après le type A est transformé comme décrit ci-dessus). Cependant, il y a trois cas qui permettent une différence:

  • Si l'original P est un type de référence, déduite A (à savoir, le type désigné par la référence) peut être plus cv-qualifié que A.
  • A peut être un autre pointeur ou pointeur sur le type d'élément qui peut être converti en déduit A par l'intermédiaire d'une conversion de qualification (conv.qual).
  • Si P est une classe, et P a le modèle-id formulaire, A peut être une classe dérivée de la déduite A. De même, si P est un pointeur vers une classe de modèle de formulaire-id, A peut être un pointeur vers une classe dérivée pointée par la a déduite.

Ici « P » est le type d'argument de la fonction de modèle: M B::*p, où le paramètre de type de modèle M est à déterminer. "A" est le type de l'argument réel: int A::*. P et A ne sont certainement pas une référence ou une classe, et le type de conversion pointeur à membre, nous aurions besoin pour que cela fonctionne est pas une conversion de qualification (qui ne décrit que const / manipulations volatiles comme X* à const X* ou int X::* à const int X::* ).

L'argument de modèle ne peut pas être déduit, et vous devez ajouter le paramètre de modèle explicite <int> à votre code.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top