problema de plantillas ('typename' como parámetro de función de plantilla no)
-
05-07-2019 - |
Pregunta
En realidad, tengo un problema al compilar alguna biblioteca con el compilador de inteligencia.
Esta misma biblioteca se ha compilado correctamente con g ++.
El problema es causado por las plantillas.
Lo que me gustaría entender es la declaración de
**typename**
como no parámetro de función de plantilla y declaración de variable dentro del cuerpo de función
ejemplo:
void func(typename sometype){..
...
typename some_other_type;
..
}
La compilación de este tipo de código produce los siguientes errores (intel), (gcc no dice): Tengo los siguientes errores
../../../libs/log/src/attribute_set.cpp(415): error: no operator "!=" matches these operands
operand types are: boost::log_st::basic_attribute_set<wchar_t>::iter<'\000'> != boost::log_st::basic_attribute_set<wchar_t>::iter<'\000'>
while (begin != end)
^
detected during instantiation of "void boost::log_st::basic_attribute_set<CharT>::erase(boost::log_st::basic_attribute_set<CharT>::iter<'\000'>, boost::log_st::basic_attribute_set<CharT>::iter<'\000'>) [with CharT=wchar_t]" at line 438
../../../boost/log/attributes/attribute_set.hpp(115): error: no operator "!=" matches these operands
operand types are: boost::log_st::basic_attribute_set<wchar_t>::iter<'\000'> != boost::log_st::basic_attribute_set<wchar_t>::iter<'\000'>
if (it != m_pContainer->end())
Lo que me gustaría entender es el uso del nombre de tipo dentro del cuerpo de funciones, declaraciones de parámetros.
ej .:
template< typename CharT >
struct basic_attribute_values_view< CharT >::implementation
{
public:
..
..
void adopt_nodes( **typename attribu**te_set_type::const_iterator& it, **typename attribut**e_set_type::const_iterator end)
{
for (; it != end; ++it)
push_back(it->first, it->second.get());
}
en un archivo diferente que he:
template< typename CharT >
class basic_attribute_set
{
friend class basic_attribute_values_view< CharT >;
//! Self type
typedef basic_attribute_set< CharT > this_type;
public:
//! Character type
typedef CharT char_type;
//! String type
typedef std::basic_string< char_type > string_type;
//! Key type
typedef basic_slim_string< char_type > key_type;
//! Mapped attribute type
typedef shared_ptr< attribute > mapped_type;
//! Value type
typedef std::pair< const key_type, mapped_type > value_type;
//! Allocator type
typedef std::allocator< value_type > allocator_type;
//! Reference type
**typedef typename allocator_type::reference reference;**
Solución
Debe usar typename
para los llamados " tipos dependientes " ;. Esos son tipos que dependen de un argumento de plantilla y no se conocen hasta que se crea una instancia de la plantilla. Probablemente se explique mejor usando un ejemplo:
struct some_foo {
typedef int bar;
};
template< typename Foo >
struct baz {
typedef Foo::bar barbar; // wrong, shouldn't compile
barbar f(); // would be fine if barbar were a type
// more stuff...
};
Esa typedef
definición de barbar
es una que requiere un Foo::bar
para que el compilador pueda verificar la plantilla en busca de errores sintácticos evidentes antes de que se instancia con un tipo concreto . La razón es que, cuando el compilador ve la plantilla por primera vez (cuando aún no se instancia con parámetros de plantilla concretos), el compilador no sabe si baz
es un tipo. Por lo que sé, podría intentar baz::bar
ser instanciado con tipos como este
struct some_other_foo {
static int bar;
};
en cuyo caso f()
se referiría a un objeto , no a un tipo, y la definición de bar<Foo
sería un sinsentido sintáctico. Sin saber si >
se refiere a un tipo, el compilador no tiene la posibilidad de verificar nada dentro de template
que esté usando directa o indirectamente <=> incluso para los errores más estúpidos hasta que se instancia <=>. Usando el <=> apropiado, <=> se ve así:
template< typename Foo >
struct baz {
typedef typename Foo::bar barbar;
barbar f();
// more stuff...
};
Ahora el compilador al menos sabe que se supone que <=> es el nombre de un tipo, lo que hace que <=> también sea un nombre de tipo. Entonces, la declaración de <=> también es sintáctica.
Por cierto, hay un problema similar con las plantillas en lugar de los tipos:
template< typename Foo >
struct baz {
Foo::bar<Foo> create_wrgl(); // wrong, shouldn't compile
};
Cuando el compilador " ve " <=> no sabe lo que es, por lo que <=> podría ser una comparación, dejando al compilador confundido acerca del <=> final. Aquí, también, debe darle al compilador una pista de que se supone que <=> es el nombre de una plantilla:
template< typename Foo >
struct baz {
Foo::template bar<Foo> create_wrgl();
};
Cuidado: Notablemente, Visual C ++ todavía no implementa la búsqueda de dos fases adecuada (en esencia: en realidad no verifica las plantillas hasta que se instancian). Por lo tanto, a menudo acepta código erróneo que pierde un <=> o un <=>.
Otros consejos
El objetivo de la palabra clave typename
es decirle al compilador que algo es un nombre de tipo, en situaciones donde no es obvio. Tome este ejemplo:
template<typename T>
void f()
{
T::foo * x;
}
¿Es T::foo
un tipo, lo que significa que estamos declarando un puntero, o es <=> una variable estática y estamos haciendo una multiplicación?
Dado que el compilador no tiene idea de qué T podría ser en el momento en que lee la plantilla, no tiene idea de cuál de los dos casos es correcto.
El estándar dicta que el compilador debe asumir el último caso, y solo interpretará <=> como un nombre de tipo si está precedido por la palabra clave <=>, como esta:
template<typename T>
void f()
{
typename T::foo* x; //Definitely a pointer.
}
Sobre su código:
void func(typename sometype)
{
.....typename some_other_type;
..
}
Si el código anterior no es parte de una plantilla, entonces no puede compilarse usando g ++, a menos que la versión anterior de g ++.
Como mi experimentado, FC9 o GNU C / ++ versión 4.2x lo informará como un error, se quejará:
typename only can be used in template code
mientras que FC8 o GNU C / ++ 4.1x pueden no.
Por favor vea
http://code.google.com/p/effocore/source/browse/trunk/devel/effo/codebase/addons/inl/include/ringed_inl.h
and
http://code.google.com/p/effocore/source/browse/trunk/devel/effo/codebase/addons/inl/include/cont_inl.h
para obtener más ejemplos de plantillas y nombres de tipos.