Pregunta

Estoy intentando crear una función:

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

donde el comportamiento varía según el tipo de p aprobada en.En particular, la versión de getClassName llamado debe depender del tipo de p.En el siguiente ejemplo, puedo llamar con éxito:

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

pero falla cuando llamo:

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

con el error:

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&)’

(CCG 4.2.4).

Si muevo la declaración de:

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

antes de hacerlo, luego se compila.Entonces,

  • ¿Por qué se requiere que getClassName( std::vector<T,A>& ) aparece antes doIt pero no getClassName( MyClass2T<T>& )
  • ¿Qué puedo hacer para hacer doIt independiente de std::vector?(Quiero poder colocar doIt en su propio encabezado y no tener que saberlo std::vector, o cualquiera de las especializaciones, que serán definidas por el usuario).

.

#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
}
¿Fue útil?

Solución

  

¿Por qué es necesario que getClassName (std :: vector & amp;) aparezca antes que doIt pero no getClassName (MyClass2T & amp;)

Se requiere una declaración de alcance para cualquier función. Cuando crea una instancia de su función de plantilla con un vector<int> espera que esté presente una función con la firma getClassName(vector<int>&) (al menos un prototipo) para que la compilación tenga éxito.

  

¿Qué puedo hacer para que sea independiente de std :: vector? (Quiero poder colocar DoIt en su propio encabezado y no tener que saber acerca de std :: vector, o cualquiera de las especializaciones, que serán definidas por el usuario)

Lea las Preguntas frecuentes sobre las plantillas . Intente colocar el prototipo de todas las funciones de plantilla dependientes de doIt antes de la primera instancia de <=>.

Otros consejos

El motivo del error es que en la creación de instancias, no se produce ninguna búsqueda de nombres no calificados para las funciones (sino sólo ADL - Búsqueda dependiente de argumentos).El contexto de creación de instancias es (tomado de 14.6.4.1/6 del estándar C++):

El contexto de creación de instancias de una expresión que depende de los argumentos de la plantilla es el conjunto de declaraciones con enlace externo declarado antes del punto de creación de instancias de la especialización de la plantilla en la misma unidad de traducción.

El punto de creación de instancias de todas esas especializaciones de plantilla que usted llamó en este caso está justo después de la definición de test (leer 14.6.4.1/1).Entonces, todas las funciones que declaraste son visibles en tu test función que utiliza una búsqueda no calificada, pero la búsqueda de ellas es en realidad diferente para las llamadas a función:

Una llamada a función que depende de un parámetro de plantilla dentro de una plantilla se busca así:

  • Los nombres del contexto de definición de plantilla se consideran tanto en la búsqueda ordinaria como en ADL.
  • Los nombres del contexto de creación de instancias se consideran solo para ADL.

Esto significa que debido a que no hay un adecuado getClassName función declarada en el contexto de definición de la plantilla, se debe encontrar una función adecuada en el contexto de instanciación usando ADL; de lo contrario, la llamada fallará y no encontrará ninguna declaración.

Búsqueda dependiente de argumentos (ADL)

Para un argumento de tipo std::vector<T>, ADL busca funciones en el espacio de nombres std y el espacio de nombres de T.poniendo el getClassName funcionar en el std El espacio de nombres funcionaría para esto (pero el Estándar no lo permite porque produce un comportamiento indefinido; esto debe hacerse solo como último recurso).

Para ver los efectos de ADL intenta llamar doIt con un vector de MyClass2 en lugar de int.Desde entonces T = MyClass2, ADL buscará en el espacio de nombres de MyClass2 para una función adecuada aceptando un std::vector<MyClass2> y tendrá éxito - a diferencia de cuando usas int, que sólo examinará std.

Para las otras llamadas a funciones, también se encuentran sus respectivas declaraciones, porque todas están declaradas en el espacio de nombres global, en el que también se definen los tipos de argumentos de las llamadas a funciones (MyClass1, MyClass2 etc).

Las preguntas frecuentes sobre C++ son buenas, pero no profundizan en las plantillas (no he encontrado ninguna mención de ADL en ellas).Hay un dedicado preguntas frecuentes sobre la plantilla que maneja algunos de los obstáculos más complejos.


Cuidado con el comportamiento indefinido

Tenga en cuenta que muchos compiladores aceptarán el código incluso cuando coloque la declaración que mostré. después el test función (en lugar de antes de ella).Pero como dice la cita estándar anterior, entonces la declaración no será parte del contexto de creación de instancias y la regla que se encuentra en 14.6.4.2/1 hay que vigilar:

Si la llamada estuviera mal formada o encontrara una mejor coincidencia, la búsqueda dentro de los espacios de nombres asociados hubiera considerado todas las declaraciones de funciones con enlace externo introducidas en esos espacios de nombres en todas las unidades de traducción, no solo considerando aquellas declaraciones que se encuentran en la definición de la plantilla y en la plantilla. contextos de creación de instancias, entonces el programa tiene un comportamiento indefinido.

Por tanto, lo que parecería funcionar sería un comportamiento indefinido.Es válido que un compilador lo acepte, pero también es válido que uno lo rechace o se bloquee y finalice.Así que observe que cualquier nombre necesario sea visible en el contexto de creación de instancias como se explica.

Espero que esto ayude.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top