Actually I've a problem with compiling some library with intel compiler.
This same library has been compiled properly with g++.
Problem is caused by templates.
What I'd like to understand is the declaration of
**typename**
as not template function parameter and variable declaration inside function body
example:
void func(typename sometype){..
...
typename some_other_type;
..
}
Compilation this kind of code produce following errors (intel),(gcc doesn't claim): I've got following errors
../../../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())
What I'd like to understand is the usage of the typename inside the body of functions, parameter declarations.
ex.:
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());
}
in different file I've:
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;**
You need to use typename
for so-called "dependent types". Those are types that depend on a template argument and are not known until the template is instantiated. It's probably best explained using an example:
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...
};
That typedef
defining barbar
is one that requires a typename
in order for the compiler to be able to check the template for blatant syntactic errors before it is instantiated with a concrete type. The reason is that, when the compiler sees the template for the first time (when it's not instantiated with concrete template parameters yet), the compiler doesn't know whether Foo::bar
is a type. For all it know, I might intent baz
to be instantiated with types like this one
struct some_other_foo {
static int bar;
};
in which case Foo::bar
would refer to an object, not a type, and the definition of baz::bar
would be syntactic nonsense. Without knowing whether Foo::bar
refers to a type, the compiler has no chance to check anything within baz
that's directly or indirectly using barbar
for even the most stupid typos until baz
is instantiated. Using the proper typename
, baz
looks like this:
template< typename Foo >
struct baz {
typedef typename Foo::bar barbar;
barbar f();
// more stuff...
};
Now the compiler at least knows that Foo::bar
is supposed to be the name of a type, which makes barbar
a type name, too. So the declaration of f()
is syntactical OK, too.
By the way, there's a similar problem with templates instead of types:
template< typename Foo >
struct baz {
Foo::bar<Foo> create_wrgl(); // wrong, shouldn't compile
};
When the compiler "sees" Foo::bar
it doesn't know what it is, so bar<Foo
could just as well be a comparison, leaving the compiler confused about the trailing >
. Here, too, you need to give the compiler a hint that Foo::bar
is supposed to be the name of a template:
template< typename Foo >
struct baz {
Foo::template bar<Foo> create_wrgl();
};
Beware: Notably Visual C++ still doesn't implement proper two-phase lookup (in essence: it doesn't really check templates until they are instantiated). Therefor it often accepts erroneous code that misses a typename
or a template
.
The point of the typename
keyword is to tell the compiler that something is a typename, in situations where it's not obvious. Take this example:
template<typename T>
void f()
{
T::foo * x;
}
Is T::foo
a type, meaning we are declaring a pointer, or is T::foo
a static variable, and we are doing a multiplication?
Since the compiler has no idea of what T could be at the time it reads the template, it has no idea which of the two cases is correct.
The standard dictates that the compiler should assume the latter case, and only interpret T::foo
as a typename if it is preceded by the typename
keyword, like this:
template<typename T>
void f()
{
typename T::foo* x; //Definitely a pointer.
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With