article turn from: C + + Proverbs: Understanding the two meanings of TypeName
- Question: What is the difference between class and TypeName in the following template declarations (templates declaration)?
template<classclass// uses "class"class// Uses "TypeName"
Answer: Nothing different. Class and TypeName mean exactly the same thing when you declare a template type parameter (the parameter for the templates). Some programmers prefer to use class at all times because it's easier to type in. Other people (including myself) prefer TypeName because it implies that this parameter is not necessarily a class type. A few developers use TypeName when any type is allowed, while retaining the class to only accept user-defined types (user-defined type). But from a C + + point of view, class and typename mean exactly the same thing when declaring a template parameter.
2. however, C + + does not always treat class and typename as equivalent. Sometimes you have to use TypeName. To understand this, we have to discuss two types of names that you will be involved in in a template.
Suppose we have a template for a function that can get an object that can be assigned to ints in a stl-compatible container (STL compliant container). This function is further assumed to simply print the value of its second element. It is a confused function that is implemented in a confused way, and as I write below, it cannot even compile, but please put it aside-there is a way to discover my stupidity:
Template<typename c>//Print 2nd element invoidprint2nd (Constc& container)//container;{//This is not valid c++! if(Container.size () >=2) {C::const_iterator iter (Container.begin ());//get iterator to 1st element++iter;//move iter to 2nd element intvalue = *iter;//copy that element to an intStd::cout << value;//Print the int}}
I highlighted two local variables (local variables) in this function, ITER and value. The type of ITER is c::const_iterator, a type that relies on the template parameter (parameter) C. The name of a template (template) that relies on a templated parameter (templates parameter) is called dependent names (dependent name). When a dependent names (dependent name) is nested inside a class (class), I call it nested dependent name (nested dependent name). C::const_iterator is a nested dependent name (nested dependent name). In fact, it is a nested dependent type name (nested dependent type name), that is, a nested dependent name (nested dependent name) that involves a type (type).
Another local variable (local variable) value in print2nd has an int type. An int is a name that does not depend on any template parameter (templates parameter). Such names are known as non-dependent names (non-dependent names). (I can't figure out why they don't call it a independent names (no dependent name). If, like me, you find the term "non-dependent" to be a disgusting thing, you resonate with me, but "non-dependent" is the term for such names, so, like me, turn your eyes and give up your self-assertion. )
Nested dependent name (nested dependent name) can cause parsing difficulties. For example, suppose we are more foolish to start print2nd in this way:
Template<typename c>void print2nd (const c&* x; ...}
This looks like we're declaring X as a local variable that points to c::const_iterator. But it does look just because we know that c::const_iterator is a type. But what if c::const_iterator is not a type? What happens if C has a static data member (static member) called Const_iterator? What if x happens to be the name of a global variable (globally variable)? In this case, the above code is not declaring a local variable, but instead becomes c::const_iterator multiplied by x! Of course, this may sound silly, but it is possible, and the person writing the C + + parser must consider all possible inputs, or even stupidity.
until c becomes known, there is no way to know whether C::const_iterator is a type or not, and C is not known when template print2nd is parsed. C + + has a rule that resolves this ambiguity: if the parser encounters a nested dependent name (nested dependent name) in a template, it assumes that the name is not a type, unless you tell it otherwise. By default, nested dependent name (nested dependent name) is not types (type). (There is an exception to this rule, I'll tell you later.)
Remember this, and then look at the beginning of print2nd:
Template<typename c>void print2nd (const c& container) { if2 // This name was assumedto// isn't be a type}
Why this is not legal C + + should now be clear. The Declaration (Declaration) of ITER is only meaningful when c::const_iterator is a type, but we do not tell C + + it is, and C + + assumes it is not. To change this situation, we have to tell C + + C::const_iterator to be a type. We put the TypeName in front of it to do this:
// This is valid C + + void print2nd (const c& container) { if2) { typename C:: Const_iterator iter (Container.begin ()); ... }}
The General rule is simple: when you involve a nested dependent type name (nested dependency type name) in the template, you must put the word typename in front of it next to it. (Again, I would like to describe an exception later.) )
TypeName should only be used to identify nested dependent type name (nested dependency type name); Other names should not be used. For example, this is a function template that obtains a container (container) and a iterator (iterator) in this container (container):
// TypeName allowed (as is "class") void F (const// TypeName not allowed// TypeName required
C is not a nested dependent type name (nested dependency type name) (it is not nested inside of something that relies on a template parameter (templated parameter)), so when declaring container it does not have to be TypeName before , but C::iterator is a nested dependent type name (nested dependent types name), so it must be typename pre-set.
The exception to the rule "TypeName must precede nested dependent type names" ("TypeName must be placed before nested dependency type name") is that TypeName does not have to be placed before a list of base classes ( base class list), or in a member initialization list (member initialization lists) as a base classes identifier (base class identifier) nested dependent type Name (nested dependency type name). For example:
Template<typename t>classDerived: PublicBase<t>:: Nested {//base class List:typename not Public://allowed ExplicitDerived (intx): Base<t>::nested (x)//base class identifier in Mem{//init. List:typename not allowedTypeName Base<t>::nested temp;//Use of nested dependent type...//name not in a base class list or}//As a base class identifier in a...//mem. init. List:typename Required};
Such contradictions are annoying, but once you gain a bit of experience in your experiences, you will hardly care about it.
Let's take a look at the last example of a typename, because it is representative of the real code you see. Let's say we're writing a function template that gets a iterator (iterator), and we're going to do a local copy of the object (an iterator) pointing to iterator temp, we can do this:
Template<typename itert>void workwithiterator (Itert iter) {typename Std::iterator_traits <itert>::value_type Temp (*iter); ...}
Don't let Std::iterator_traits<itert>::value_type scare you. That is just a standard traits class (the normal attribute Class) use, in C + + is said to be "the type of thing pointed to by objects of type Itert" ("The object is of the kind Itert") The type of the object being pointed to "). This statement declares a local variable (temp) that is the same type as Itert objects, and initializes the temp with the object that ITER points to. If Itert is vector<int>::iterator,temp, it is the int type. If Itert is list<string>::iterator,temp, it is a string type. Because Std::iterator_traits<itert>::value_type is a nested dependent type name (nested dependency type name) (Value_type nested in Iterator_ Traits<itert> inside, and Itert is a template parameter (parameter), we have to let it be typename pre-placed.
If you think that reading Std::iterator_traits<itert>::value_type is annoying, imagine that it is the same thing that represents it. If you are like most programmers and are afraid to enter it multiple times, then you need to create a typedef. For traits member names (attribute member names) such as value_type, a common practice is that typedef name is the same as traits member name, so a local typedef like this is usually defined as:
template<typename itert>void Workwithiterator (Itert iter) {typedef typename std::iterator_traits <IterT> ::value_type value_type; value_type temp ( *iter ); ...}
Many programmers initially found that the "typedef typename" is not well-tied, but it is a reasonable accompanying result involving nested dependent type names (nested dependency type name) rules. You'll get used to it pretty quickly. You have a strong motive after all. How much time do you need to enter TypeName Std::iterator_traits<itert>::value_type?
As a conclusion, I should mention the difference between the compiler and the compiler about the execution of the rules around TypeName. Some compilers accept code that is missing when required TypeName, while some compilers accept code that is not allowed to TypeName, and a few (usually old) refuse to TypeName appear where it is necessary to appear. This means that the interaction of typename and nested dependent type names (nested dependent type name) can cause some minor portability problems.
Things to Remember
• Class and TypeName are interchangeable when declaring template parameters (templates parameter).
• Use TypeName to identify nested dependent type names (nested dependency type name), either in base class lists (base classes list) or in a member initialization list (member initialization lists) as a The base class identifier.
[Go] C + + Proverbs: Understanding the two meanings of TypeName