Question

La dernière question que je posais était quelque chose que je suis tombé sur en essayant de comprendre une autre chose ... que je peux aussi ne pas comprendre (pas mon jour).

Ceci est tout à fait une longue déclaration de question, mais au moins je l'espère, cette question pourrait se révéler utile pour beaucoup de gens et non seulement moi.

Le code que j'ai est le suivant:

template <typename T> class V;
template <typename T> class S;

template <typename T>
class V
{
public:
 T x;

 explicit V(const T & _x)
 :x(_x){}

 V(const S<T> & s)
 :x(s.x){}
};

template <typename T>
class S
{
public:
 T &x;

 explicit S(V<T> & v)
 :x(v.x)
 {}
};

template <typename T>
V<T> operator+(const V<T> & a, const V<T> & b)
{
 return V<T>(a.x + b.x);
}

int main()
{
 V<float> a(1);
 V<float> b(2);
 S<float> c( b );

 b = a + V<float>(c); // 1 -- compiles
 b = a + c;           // 2 -- fails
 b = c;               // 3 -- compiles

 return 0;
}

Expressions 1 et 3 fonctionnent parfaitement, alors que l'expression 2 ne compile pas.

Si je comprends bien, ce qui se passe est:

Expression 1

  1. c est converti est implicitement const en utilisant une séquence de conversion standard (composé sur un seul conversion de qualification ).
  2. V<float>(const S<T> & s) est appelé et l'objet const V<float> temporel généré (Appelons-le t ). Il est déjà const qualifié parce qu'il est une valeur temporelle.
  3. a est converti en const de façon similaire à c .
  4. operator+(const V<float> & a, const V<float> & b) est appelée, ce qui entraîne dans un temporel de type const V<float> que l'on peut appeler q .
  5. le V<float>::operator=(const & V<float>) par défaut est appelé.

Suis-je OK jusqu'à ici? Si je fait même la plus subtile erreur s'il vous plaît, laissez-moi savoir, car je suis en train d'acquérir une compréhension au sujet de coulée implicite aussi profond que possible ...

Expression 3

  1. c est converti en V<float>. Pour cela, nous avons une séquence de conversion définie par l'utilisateur:
    1.1. première conversion standard. S<float> à const S<float> par conversion de qualification
    1.2. conversion définie par l'utilisateur: const S<float> à V<float> via V<float>(const S<T> & s) constructeur
    . 1,3 secondes conversion standard. V<float> à const V<float> par conversion de qualification
  2. le V<float>::operator=(const & V<float>) par défaut est appelé.

Expression 2?

Ce que je ne comprends pas pourquoi il y a un problème avec la deuxième expression. Pourquoi la séquence suivante pas possible?

  1. c est converti en V<float>. Pour cela, nous avons une séquence de conversion définie par l'utilisateur:
    1.1. initiale conversion standard. S<float> à const S<float> par conversion de qualification
    1.2. conversion définie par l'utilisateur: const S<float> à V<float> via V<float>(const S<T> & s) constructeur
    . 1.3. conversion standard final: V<float> à const V<float> par conversion de qualification.
  2. Les étapes 2 à 6 sont les mêmes que dans le cas d'une expression.

Après avoir lu la norme C ++ je pensais: « hey! peut-être le problème doit à 13.3.3.1.2.3 avec! qui stipule que:

  

Si la conversion définie par l'utilisateur est spécifié par une fonction de conversion de modèle, la deuxième séquence de conversion standard doit avoir rang de correspondance exacte.

Mais ce ne peut être le cas puisque la conversion de qualification a rang exact match ...

Je ne sais vraiment pas la moindre idée ...

Eh bien, si vous avez la réponse ou non, vous remercie pour la lecture jusqu'à ici:)

Était-ce utile?

La solution

Edric a fait remarquer, les conversions ne sont pas pris en compte lors de la déduction argument modèle. Ici, vous avez deux contextes où le paramètre le modèle T peut être déduit du type des arguments:

template<class T>
v<T> operator+(V<T> const&, V<T> const&);
               ~~~~~~~~~~~  ~~~~~~~~~~~~

Mais vous essayez d'appeler ce modèle de fonction avec un V<float> sur le côté gauche et un S sur le côté droit. déduction argument modèle T = résultats dans flotteur pour le côté gauche et vous obtiendrez une erreur pour le côté droit parce qu'il n'y a pas de T pour que V<T> est égal à S<T>. Ayant un caractère non déductible au titre d'argument de modèle et le modèle est tout simplement ignoré.

Si vous souhaitez autoriser les conversions de votre opérateur + ne doit pas être un modèle. Il y a l'astuce suivante: Vous pouvez le définir comme un ami en ligne à l'intérieur du modèle de classe V:

template<class T>
class V
{
public:
   V();
   V(S<T> const&); // <-- note: no explicit keyword here

   friend V<T> operator+(V<T> const& lhs, V<T> const& rhs) {
      ...
   }
};

De cette façon, l'opérateur est un modèle plus. Donc, il n'y a pas besoin de déduction argument modèle et votre appel devrait fonctionner. L'opérateur se trouve par ADL (de recherche dépendant de l'argument) parce que le côté gauche est un V<float>. Le côté droit est correctement converti en V<float> ainsi.

Il est également possible de désactiver la déduction des arguments de modèle pour un argument spécifique. Par exemple:

template<class T>
struct id {typedef T type;};

template<class T>
T clip(
   typename id<T>::type min,
   T value,
   typename id<T>::type max )
{
   if (value<min) value=min;
   if (value>max) value=max;
   return value;
}

int main() {
   double x = 3.14;
   double y = clip(1,x,3); // works, T=double
}

Même si le type du premier et dernier argument est un entier, ils ne sont pas pris en compte lors déduction de l'argument de modèle parce id<T>::type n'est pas un soi-disant * déduisent context`. Ainsi, T ne se déduit selon le second argument, qui se traduit par T = double avec aucune contradiction.

Autres conseils

Lors de l'examen des matchs de modèle, les conversions implicites ne sont pas utilisés. Par conséquent, dans l'exemple simple suivant:

template < typename T >
void foo( T t1, T t2 ) { /* do stuff */ }

int main( int argc, char ** argv ) {
    foo( 1, 1.0 );
    return 0;
}

Ce ne compilera pas même si l'un des arguments pourrait être converti implicitement à l'autre type (int <-> double).

Juste une supposition, mais peut-être le compilateur ne peut pas distinguer entre la conversion de V-> S ou de S> V tout en essayant de comprendre comment ajouter un + c dans l'expression 2. Vous supposez le compilateur sera assez intelligent pour choisir celui qui permet la compilation de procéder à cause du reste des fonctions disponibles, mais le compilateur est probablement pas « lecture avant » (pour ainsi dire), et se confond avec l'ambiguïté de la conversion vers le haut avant en essayant de trouver l'opérateur '+'.

Bien sûr, si vous avez ajouté l'erreur de compilation, il pourrait aider à clarifier le problème aussi ...

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