Domanda

Sto cercando di creare una funzione:

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

in cui il comportamento varia a seconda del tipo di p passato.In particolare, la versione di getClassName chiamato dipende dal tipo di p.Nell'esempio seguente, posso chiamare correttamente:

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

ma fallisce quando l'ho chiamata:

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

con il messaggio di errore:

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).

Se sposto la dichiarazione di:

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

prima doIt -- poi si compila.Così,

  • Perché è necessario che getClassName( std::vector<T,A>& ) viene visualizzata la prima doIt ma non getClassName( MyClass2T<T>& )
  • Cosa posso fare per rendere doIt indipendente di std::vector?(Voglio essere in grado di posto doIt in intestazione e non sono a conoscenza std::vector, o una qualsiasi delle specializzazioni, che sarà definito dall'utente).

.

#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
}
È stato utile?

Soluzione

  

Perché è necessario che getClassName (std :: vector & amp;) sia visualizzato prima di doIt ma non getClassName (MyClass2T & amp;)

Per qualsiasi funzione è richiesta una dichiarazione nell'ambito. Quando si crea un'istanza della funzione del modello con vector<int>, si attende che sia presente una funzione con la firma getClassName(vector<int>&) (almeno un prototipo) affinché la compilazione abbia esito positivo.

  

Cosa posso fare per renderlo indipendente da std :: vector? (Voglio essere in grado di inserire doIt nella sua intestazione e non devo sapere di std :: vector o di nessuna delle specializzazioni, che saranno definite dall'utente)

Leggi le Domande frequenti sui modelli . Prova a mettere il prototipo di tutte le funzioni di template dipendenti di doIt prima della prima istanza di <=>.

Altri suggerimenti

Il motivo è che durante la creazione dell'istanza, senza nome non qualificato ricerca per le funzioni di verificarsi (ma solo ADL Argomenti di Ricerca Dipendente).La creazione di istanze di contesto (tratto da 14.6.4.1/6 del C++ Standard):

La creazione di un ambito di espressione che dipende dal modello di argomenti è la serie di dichiarazioni con collegamento esterno dichiarata prima del punto di creazione di un'istanza del modello di specializzazione nella stessa unità di traduzione.

Il punto di creazione di istanze di tutti coloro che modello specializzazioni si chiama in questo caso è solo dopo la definizione di test (leggi 14.6.4.1/1).Così, tutte le funzioni dichiarate sono visibili in test funzione utilizzando non qualificato di ricerca, ma la ricerca per loro è veramente diverso per le chiamate di funzione:

La chiamata di una funzione che dipende da un parametro di modello all'interno di un modello è guardato come questo:

  • Nomi dalla definizione di un modello di contesto sono considerati da, sia di ordinaria che di ricerca e ADL.
  • Nomi di istanza contesto sono considerati solo per ADL.

Questo significa che, poiché non vi è nulla di adatto getClassName funzione dichiarata nella definizione del contesto del modello, di una opportuna funzione è la creazione di istanze contesto con ADL - altrimenti la chiamata avrà esito negativo e non trovare alcuna dichiarazione.

Argomento di Ricerca Dipendente (ADL)

Per un argomento di tipo std::vector<T>, ADL ricerca per le funzioni di spazio dei nomi std e lo spazio dei nomi di T.Mettere il getClassName funzione in std spazio dei nomi per questo (ma così facendo non è consentito dalla Norma perché i rendimenti di un comportamento indefinito - questo dovrebbe essere fatto solo come ultima risorsa).

Per vedere gli effetti di ADL provo a chiamare doIt con un vettore di MyClass2 invece di int.Da allora T = MyClass2, ADL ricerca nello spazio dei nomi di MyClass2 per una opportuna funzione di accettazione di una std::vector<MyClass2> e riuscirà - rispetto a quando si utilizza int, che sarà solo guardare std.

Per le altre chiamate di funzione, le loro rispettive dichiarazioni sono trovato troppo, perché sono tutti dichiarati nel namespace globale, in cui l'argomento tipi di chiamate di funzione sono definiti troppo (MyClass1, MyClass2 ecc).

Il C++ FAQ è buona, ma non andare in profondità i modelli (non ho trovato alcuna menzione di ADL in esso).C'è un dedicato modello faq che gestisce alcune delle più intricate insidie.


Attenzione, del comportamento indefinito

Nota che molti compilatori accettare il codice anche quando si inserisce la dichiarazione che ho mostrato dopo il test funzione (invece di prima).Ma, come Standard sopra citazione dice, poi la dichiarazione di non essere parte della creazione di un'istanza di contesto e stato trovato in 14.6.4.2/1 è da guardare:

Se la chiamata sarebbe malformati o vorresti trovare una corrispondenza migliore era la ricerca all'interno di spazi dei nomi associato a considerare tutte le dichiarazioni di funzione con collegamento esterno introdotto in quegli spazi in tutte le unità di traduzione, non solo considerando quelle dichiarazioni contenute nella definizione del modello e il modello di istanza contesti, quindi il programma ha un comportamento indefinito.

Così, quello che potrebbe sembrare al lavoro sarebbe un comportamento indefinito.Valido per un compilatore per accettare, ma è altrettanto valido per un rifiuto o un arresto anomalo e terminare.Così guarda che qualsiasi nome necessaria è infatti visibile la creazione di contesto, come spiegato.

Spero che questo aiuta.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top