Frage

Ich versuche, eine Funktion zu erstellen:

template <typename T>
void doIt( T*& p )
{
   if ( !p ) { return; }
   T& ref = *p;
   getClassName( ref );
}

, wo das Verhalten nach der Art der p gebenen variiert. Insbesondere die Version von getClassName genannt sollte von der Art der p abhängen. Im folgende Beispiel kann ich erfolgreich nennen:

doIt<myClass1>( myClass1*& )
doIt<myClass1<int> >( myClass1*& )
doIt<myClass2>( myClass2*& )
doIt<myClass2<int> >( myClass2*& )

aber es funktioniert nicht, wenn ich rufe:

doIt< std::vector<int, std::allocator<int> > >( std::vector<int, std::allocator<int>>*& )

mit dem Fehler:

a.cxx: In function ‘void doIt(T*&) [with T = std::vector<int, std::allocator<int> >]’:
ba.cxx:87:   instantiated from here
a.cxx:33: error: invalid initialization of reference of type ‘MyClass1&’ from expression of type ‘std::vector<int, std::allocator<int> >’
a.cxx:16: error: in passing argument 1 of ‘const char* getClassName(MyClass1&)’

(gcc 4.2.4).

Wenn ich mich bewege die Deklaration:

template<typename T, typename A>
char const* getClassName( std::vector<T,A>& ) { printf("std::vector<T,A>\n"); return NULL; }

vor doIt - dann kompiliert. Also,

  • Warum ist erforderlich, dass getClassName( std::vector<T,A>& ) vor doIt erscheint aber nicht getClassName( MyClass2T<T>& )
  • Was kann ich tun, um doIt unabhängig von std::vector zu machen? (Ich möchte in der Lage seinen doIt in seinem eigenen Header zu platzieren und müssen nicht über std::vector wissen, oder eine der Spezialisierungen, die vom Benutzer definiert werden).

.

#include <stdio.h>
#include <assert.h>
#include <vector>

//template<typename T>
//char const* getClassName( T& );

//template<typename T, typename A>
////char const* getClassName( std::vector<T,A>& ) { printf("std::vector<T,A>\n"); return NULL; }

#if 1
// ---------  MyClass2
struct MyClass1
{};

char const* getClassName( MyClass1& ) { printf("MyClass1\n"); return NULL; }

// ---------  MyClass1T
template< typename T>
struct MyClass1T
{};

template<typename T>
char const* getClassName( MyClass1T<T>& ) { printf("MyClass1T<T>\n"); return NULL; }
#endif


template <typename T>
void doIt( T*& p )
{
   if ( !p ) { return; }
   T& ref = *p;
   getClassName( ref );
}


// ---------  MyClass2
struct MyClass2
{};


// declared after doIt, OK.
char const* getClassName( MyClass2& ) { printf("MyClass2\n"); return NULL; }

// ---------  MyClass2T
template< typename T>
struct MyClass2T
{};

// declared after doIt, OK.
template<typename T>
char const* getClassName( MyClass2T<T>& ) { printf("MyClass2T<T>\n"); return NULL; }

template<typename T, typename A>
char const* getClassName( std::vector<T,A>& ) { printf("std::vector<T,A>\n"); return NULL; }



void test()
{
#if 1
   MyClass1 mc1;
   MyClass1* mc1p = &mc1;
   doIt( mc1p );

   MyClass2 mc2;
   MyClass2* mc2p = &mc2;
   doIt( mc2p );

   MyClass1T<int> mc1t;
   MyClass1T<int>* mc1tp = &mc1t;
   doIt( mc1tp );

   MyClass2T<int> mc2t;
   MyClass2T<int>* mc2tp = &mc2t;
   doIt( mc2tp );

   // Nested templates are OK.
   MyClass2T<MyClass1> mc2t2;
   MyClass2T<MyClass1>* mc2tp2 = &mc2t2;
   doIt( mc2tp2 );
#endif

#if 1
   std::vector<int> v;
   std::vector<int>* vp = &v;
   doIt( vp );                   // FAIL!
#endif
}
War es hilfreich?

Lösung

  

Warum ist es erforderlich, dass getClassName (std :: vector &) erscheint vor doIt aber nicht getClassName (MyClass2T &)

Eine Erklärung in ihrem Umfang ist für jede Funktion erforderlich. Wenn Sie Ihre Template-Funktion mit einem vector<int> instanziiert erwartet es eine Funktion mit dem Signatur getClassName(vector<int>&) vorhanden zu sein (mindestens einem Prototyp) für die Kompilierung erfolgreich zu sein.

  

Was kann ich machen doIt unabhängig von std :: vector Sie tun? (Ich möchte in der Lage sein doIt in seinen eigenen Header zu platzieren und müssen nicht wissen, über std :: vector, oder eine der Spezialisierungen, die vom Benutzer definiert werden)

Lesen Sie die FAQ auf Vorlagen . Versuchen Sie, den Prototyp aller doIt der abhängigen Template-Funktionen vor der ersten Instanziierung doIt setzen.

Andere Tipps

Der Grund für das Scheitern ist bei der Instanziierung, dass keine unqualifizierten Namen-Suche für die Funktionen auftritt (aber nur ADL - Argument abhängiger Lookup). Die Instanziierung Zusammenhang (aus 14.6.4.1/6 des C ++ Standard):

  

Die Instanziierung Kontext eines Ausdruck, der auf der Vorlage Argumenten hängt die Menge der Erklärungen mit externer Bindung vor dem Punkt der Instantiierung der Template-Spezialisierung in derselben Übersetzungseinheit erklärt.

Der Punkt der Instanziierung all diese Vorlage Spezialisierungen Sie in diesem Fall genannt ist kurz nach der Definition von test (lesen 14.6.4.1/1). So sind alle Funktionen, die Sie deklariert sind in Ihrer Funktion test unqualifizierte Lookup verwenden, aber Lookup für sie ist tatsächlich unterschiedlich für die Funktionsaufrufe:

Ein Funktionsaufruf, der innerhalb einer Vorlage auf einem Template-Parametern abhängig ist, wie dies nachgeschlagen:

  • Namen aus dem Template-Definition Kontext sowohl von gewöhnlichen Lookup und ADL berücksichtigt werden.
  • Namen von Instanziierung Kontext betrachtet werden nur für ADL.

Das bedeutet, dass, weil es keine geeignete getClassName Funktion in der Definition Kontext der Vorlage, eine geeignete Funktion im Instanziierung Kontext ADL werden muss gefunden deklariert ist - sonst wird der Anruf fehlschlagen und keine Erklärung finden.

Argument abhängige Lookup (ADL)

Für ein Argument vom Typ std::vector<T>, ADL sucht nach Funktionen im Namensraum std und der Namensraum von T. Inbetriebnahme der getClassName Funktion in den std Namespace für das funktionieren würde (aber durch die Norm so nicht erlaubt zu tun, weil das zu undefiniertem Verhalten ergibt - dies sollte nur als letztes Mittel durchgeführt werden).

Um die Auswirkungen von ADL zu sehen versuchen doIt zu nennen, mit einem Vektor von MyClass2 statt int. Seitdem T = MyClass2, sucht ADL im Namensraum von MyClass2 für eine geeignete Funktion eines std::vector<MyClass2> akzeptieren und erfolgreich sein wird - im Gegensatz zu, wenn Sie verwenden int, die nur in std aussehen werden.

Für die anderen Funktionsaufrufe, die entsprechenden Erklärungen zu alle gefunden werden, weil sie alle in dem globalen Namensraum deklariert werden, in denen die Argumenttypen der Funktionsaufrufe zu definiert sind (MyClass1, MyClass2 usw.).

Die C ++ FAQ ist gut, aber es geht nicht tief in den Vorlagen (nicht jede Erwähnung von ADL darin gefunden). Es ist ein dedizierter template faq , dass einige der komplizierteren Gefahren Griffe .


Vorsicht vor undefinierten Verhalten

Beachten Sie, dass viele Compiler den Code auch akzeptieren, wenn Sie diese Erklärung setzen i zeigte nach die test Funktion (statt davor). Aber wie das oben Standard-Zitat sagt, dann ist die Erklärung nicht Teil des Instanziierung Kontext und die Regel in 14.6.4.2/1 gefunden werden soll beobachtet werden:

  

Wenn der Anruf schlecht gebildet würde oder wäre eine bessere Abstimmung hatte den Nachschlag in den dazugehörigen Namensräumen als alle Funktionsdeklarationen mit externer Bindung in dem Namespaces in allen Übersetzungseinheiten eingeführt finden, nicht nur die Erklärungen in der Vorlage gefunden Berücksichtigung Definition und Template-Instantiierung Kontexte, dann hat das Programm nicht definiertes Verhalten.

So was undefiniertes Verhalten wäre scheint zu funktionieren. Es ist gültig für einen Compiler, es zu akzeptieren, aber es ist ebenfalls gültig für ein, ihn abzulehnen oder zum Absturz bringen und zu beenden. So beobachten, dass jeder benötigt Name am Instanziierung Kontext tatsächlich sichtbar ist, wie erläutert.

Hope, das hilft.

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