Frage

Die letzte Frage, die ich fragte, war etwas, das ich gestolpert, wenn sie versuchen eine andere Sache zu verstehen ... das kann ich auch nicht verstehen (nicht mein Tag).

Dies ist eine recht lange Frage Aussage, aber zumindest hoffe ich diese Frage für viele Menschen nützlich sein könnte und nicht nur mich.

Der Code, den ich habe, ist die folgende:

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

Ausdrücke 1 und 3 arbeiten perfekt, während die Expression 2 lässt sich nicht kompilieren.

Wenn ich richtig verstanden habe, was passiert ist:

Ausdruck 1

  1. c ist zu const implizit konvertiert wird, indem eine Standard-Konvertierungssequenz (bestehend nur auf eine Qualifikation Umwandlung ).
  2. V<float>(const S<T> & s) genannt wird und eine zeitliche die const V<float> Objekt erzeugt (nennen wir es t ). Es ist bereits const qualifiziert, weil es ein zeitlicher Wert ist.
  3. a umgewandelt wird in ähnlicher Weise const c .
  4. operator+(const V<float> & a, const V<float> & b) genannt wird, was zu einem zeitlichen vom Typ const V<float> dass wir nennen können q .
  5. der Standard V<float>::operator=(const & V<float>) aufgerufen wird.

Am I OK, um hier oben? Wenn ich selbst die subtilsten Fehler gemacht Bitte lassen Sie mich wissen, denn ich bin versucht, ein Verständnis über implizites Casting so tief wie möglich zu gewinnen ...

Ausdruck 3

  1. c auf V<float> umgewandelt. Dafür haben wir eine benutzerdefinierte Umwandlungsfolge:
    1.1. erste Standard-Konvertierung. S<float> zu const S<float> über Qualifizierungs Umwandlung
    1.2. benutzerdefinierte Konvertierung: const S<float> zu V<float> über V<float>(const S<T> & s) Konstruktor
    . 1,3 Sekunden Standard-Konvertierung. V<float> zu const V<float> über Qualifikation Umwandlung
  2. der Standard V<float>::operator=(const & V<float>) aufgerufen wird.

Expression 2?

Was ich nicht verstehe, ist, warum es ein Problem mit dem zweiten Ausdruck ist. Warum ist die folgende Sequenz nicht möglich?

  1. c auf V<float> umgewandelt. Dafür haben wir eine benutzerdefinierte Umwandlungsfolge:
    1.1. anfängliche Standard-Konvertierung. S<float> zu const S<float> über Qualifizierungs Umwandlung
    1.2. benutzerdefinierte Konvertierung: const S<float> zu V<float> über V<float>(const S<T> & s) Konstruktor
    . 1.3. endgültige Standard-Konvertierung: V<float> zu const V<float> über Qualifikation Umwandlung.
  2. Schritte 2 bis 6 sind die gleichen wie im Fall des Ausdruckes 1.

Nach dem Lesen der C ++ Standard Ich dachte: ‚Hey! vielleicht das Problem muss an mit 13.3.3.1.2.3!‘ in dem es heißt:

  

Wenn die benutzerdefinierte Konvertierung durch eine Schablone Konvertierungsfunktion angegeben ist, muss die zweite Standard-Umwandlungsfolge genaue Übereinstimmung Rang hat.

Aber das kann nicht der Fall sein, da die Qualifikation Umwandlung genaue Übereinstimmung Rang hat ...

ich wirklich keine Ahnung haben ...

Nun, ob Sie die Antwort haben oder nicht, dankt Ihnen für das Lesen von bis zu hier:)

War es hilfreich?

Lösung

Wie Edric wies darauf hin, Konvertierungen werden nicht während der Vorlage Argument Abzug berücksichtigt. Hier haben Sie zwei Kontexte, in denen die Template-Parameter T können von der Art des Arguments abgeleitet werden:

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

Aber Sie versuchen, diese Funktion Vorlage aufzurufen mit einem V<float> auf der linken Seite und einen S auf der rechten Seite. Vorlage Argument Abzug ergibt T = Schwimmer für die linke Seite und Sie werden einen Fehler für die rechte Seite bekommen, weil es keine T ist, so dass V<T> S<T> entspricht. Dies qualifiziert als Vorlage Argument Abzug Versagen und die Vorlage wird einfach ignoriert.

Wenn Sie Conversions Ihren Operator +, damit nicht eine Vorlage sein. Es gibt den folgenden Trick: Sie es als Inline-Freund innerhalb der Klassenvorlage für V definieren:

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

Auf diese Weise ist der Bediener eine Vorlage nicht mehr. So gibt es keine Notwendigkeit für die Vorlage Argument Abzug und Ihr Aufruf funktionieren soll. Der Bediener wird durch ADL (argument abhängige lookup), weil die linke Seite ist ein V<float> gefunden. Die rechte Seite an einem mit V<float> auch umgesetzt wird.

Es ist auch möglich zu deaktivieren Vorlage Argument Abzug für ein bestimmtes Argument. Zum Beispiel:

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
}

Auch wenn der Typ des ersten und letzten Argument ist ein int, werden sie nicht während der Vorlage Argument Abzug in Betracht gezogen, weil id<T>::type ist keine so genannte * ableitbar context`. So wird T nur nach dem zweiten Argument abgeleitet, die Ergebnisse in T = Doppel ohne Widersprüche.

Andere Tipps

Wenn Vorlage Streichhölzer unter Berücksichtigung implizite Konvertierungen werden nicht verwendet. Daher wird in dem folgenden einfachen Beispiel:

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

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

Das wird nicht kompiliert, obwohl entweder Argument implizit auf die andere Art (int <-> double) umgewandelt werden können.

Nur eine Vermutung, aber vielleicht kann der Compiler nicht zwischen der Umwandlung von V-> S oder von S-> V unterscheidet bei dem Versuch, herauszufinden, wie man ein + c in Ausdruck hinzuzufügen 2. Sie den Compiler vorausgesetzt wird intelligent genug, um die eine auswählen, die die Zusammenstellung aufgrund des Restes der verfügbaren Funktionen gehen kann, aber der Compiler ist wahrscheinlich nicht „lesen ahead“ (sozusagen), und ist mit der Mehrdeutigkeit der Aufwärtswandlung immer verwirrt vor versuchen, den '+' Operator zu finden.

Natürlich, wenn Sie die Kompilierung-Fehler hinzugefügt, könnte es helfen, das Problem zu ...

zu klären
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top