C + + typename usage __c++

Source: Internet
Author: User
C + + typename usage Directory common usage of TypeName

For typename this keyword, if you are familiar with C + + templates, you will know that it has one of the most common uses (code is excerpted from C + + Primer):

    Implement Strcmp-like Generic compare function
    //returns 0 if the values are equal, 1 if V1 is larger,-1 if V1 is Smaller
    template <typename t>
    int compare (const t &V1, const T &v2)
    {
         if (V1 < v2) retur n-1;
         if (V2 < v1) return 1;
         return 0;
    }

Perhaps you will think of the above code in the TypeName changed to class is also OK, yes. Then there is the question, the difference between these two ways. After viewing C + + primer, you find that the two are exactly the same. So why should C + + support both of these approaches at the same time? Since class is already there early, why introduce the TypeName keyword. Sources of TypeName

For some friends who have earlier contacted C + +, you may know that many older compilers only support class when the C + + standard is not yet unified, because C + + does not have typename keywords at that time. I remember when I was learning C + + in a certain C + + books have seen similar considerations, tell us if the use of typename when the compiler error, then class can be changed.

Everything is due to history.

Stroustrup when he first drafted the template specification, he took into account the introduction of a new keyword for the type parameters of the template, but doing so would probably break many of the programs that have been written (because class has been in use for a long time). But the more important reason is that, at the time, class was fully qualified for this requirement of the template, so in order to avoid causing unnecessary trouble, he chose to compromise and reuse the existing class keywords. So before the ISO C + + standard comes out, there is only one way to specify the type parameters of the template, and that is to use class. This also explains why many older compilers only support class.

But for many people, class is not always used because, for its intended purpose, it is to distinguish the built-in type of the language from the one used to declare a user custom type. So for the following definition of the template function (in relation to the previous example, only TypeName is replaced with Class):

1 template <class t>
2 int compare (const t &V1, const T &V2)
3 {
4     if (V1 < v2) return-1 ;
5     if (V2 < v1) return 1;
6 return     0;
7}

It looks as if the template's parameters should support only user- defined types , so it's always a bit strange to use a language built-in type or pointer to invoke the template function (although there is no error):

1 int v1 = 1, v2 = 2;
2 int ret = compare (v1, v2);
3
4 int *pv1 = NULL, *pv2 = NULL;
5 ret = compare (PV1, Pv2);

The curious reason for this is that the meaning of class in classes and templates appears to be inconsistent, which is for user-defined types, and the latter contains language-built types and pointers. And because of that, people seem to feel that not introducing a new keyword at the time could be a mistake.

This is one of the factors that prompted the standard committee to introduce new keywords, but there is another, more important reason, related to the first line of code in the article. some key concepts

Before we uncover the veil of truth, there is a sense of mystery, because there are several important concepts that need to be introduced in order to better understand the C + + standard. qualified and unqualified names

The qualified name (qualified name), which is called semantic, is the name that qualifies the namespace. Look at the code below, cout and Endl are qualified names:

1 #include <iostream>
2
3 int main ()  {
4     std::cout << "Hello world!" << Std::endl ;
5}

cout and Endl have std::, which limits the Std namespace, and therefore calls it a qualified name.

If the preceding code is preceded by a using Std::cout; or using namespace std, and then using only cout and Endl, there is no space before the std::, so cout and Endl at this time are called unqualified names ( Unqualified name). dependent names and non-dependent names

The dependency name (dependent name) is the name that relies on the template parameter, rather than the dependent name (non-dependent name), which is not dependent on the name of the template parameter. Look at the following code:

1 template <class t>
 2 class MyClass {
 3     int i;
 4     vector<int> VI;
 5     vector<int>::iterator Vitr;
 6
 7     T t;
 8     vector<t> VT;
 9     vector<t>::iterator Viter;
10};

Because it is a built-in type, the first three defined types in the class are known when declaring the template class. However, for the next three-line definitions, they are only known when the template is instantiated, because they all depend on the template parameter T. Therefore, T, vector<t> and vector<t>::iterator are called dependent names. The first three definitions are called non-dependent names.

A bit more complicated if the TypeDef T U is used; u u;, although t does not appear again, but U is still a dependent name. This shows that, whether directly or indirectly, as long as the template parameters rely on, the name is the dependency name. class Scopes

When you access a name in a class outside of a class, you can use the class scope operator, which typically has three kinds of calls, such as myclass::name: static data members, static member functions, and nested types:

1 struct MyClass {
2     static int A;
3     static int B ();
4     typedef int C;
5}

Myclass::a, myclass::b, myclass::c respectively correspond to the above three species. The real reason for introducing TypeName

To end the discussion of the above three concepts, let us then uncover the mysteries of TypeName. An example

After Stroustrup drafted the original template specification, people were more carefree about using class for a long time. However, with the advent of standardized C + + work, a definition of templates was found:

1 template <class t>
2 void foo () {
3     t::iterator * ITER;
4     //...
5}

What is the purpose of this piece of code. Most people's first reaction may be: The author wants to define a pointer iter, which points to a type that is contained in the class scope T iterator. There may be such a structure containing the iterator type:

1 struct Containsatype {
2     struct iterator {/*...*/};
3     //...
4};

And then instantiate foo like this:

In this way, ITER's line of code is obvious, it is a containsatype::iterator type of pointer. So far, we have a good guess, and everything looks good. problems surfaced

In the class scope section, we introduced three names, and since MyClass is already a complete definition, the type of the compile period can be determined, meaning that myclass::a these names are known to the compiler.

But what if it's like t::iterator. T is the type parameter in the template, and it will not know what type it is until the template is instantiated, let alone the internal iterator. By introducing the previous class Scope section, we can see that t::iterator can actually be any of the following three types: static data member static member function nested type

The containsatype::iterator in the previous example is a nested type, and there is no problem at all. But what if it's a static data member. If you instantiate the type of the Foo template function like this:

1 struct Containsanothertype {
2     static int iterator;
3     //...
4};

Then instantiate the type parameters of Foo:

1 foo ();

So, T::iterator * ITER; is instantiated by the compiler as Containsanothertype::iterator * ITER; Preceded by a static member variable rather than a type, this becomes a multiplication expression, except that ITER does not have a definition here, and the compiler complains:

Error C2065: ' iter ': undeclared identifier

But if ITER is a global variable, then this line of code will be completely correct, it represents an expression that calculates the multiplication of two numbers, and the return value is discarded.

The same line of code can be interpreted in two completely different ways, and there is absolutely no way to differentiate them until the template is instantiated, which is definitely a breeding ground for a variety of bugs. At this point the C + + Standard committee can no longer resist, and to instantiate to know exactly which way to explain the above code, the Committee decided to introduce a new keyword, this is typename. long-awaited begins.

Let's take a look at [C + + standard][3]:

A name used in a template declaration or definition and this is dependent on a template-parameter are assumed not to name a Type unless the applicable name lookup finds a type name or the name is qualified by the keyword typename.

For a name that is dependent on a template parameter for a template definition, the compiler will treat the name as a type only if the type name exists in the instantiated parameter, or when the name is decorated with the TypeName keyword. In addition to these two cases, it will never be considered a type.

So if you want to tell the compiler directly that the T::iterator is a type and not a variable, just use the TypeName modifier:

1 template 
2 void foo () {
3     typename T::iterator * ITER;
4     //...
5}

This allows the compiler to determine that the t::iterator is a type and no longer needs to wait for the instantiation period to be determined, thus eliminating the ambiguity mentioned earlier. The handling of error cases by different compilers

But if you still use Containsanothertype to instantiate Foo, the former has only one static member variable called iterator, and the latter needs a type, and the result is what. I experimented on Visual C + + 2010 and g++ 4.3.4, and the results were as follows:

Visual C + + 2010 still reports the same error as before:

Error C2065: ' iter ': undeclared identifier

Although we have already used the keyword typename to tell the compiler that iterator should be a type, the compiler chose to ignore this keyword when instantiating the template with a struct that defined the iterator variable. The error occurred only because ITER was not defined.

To see how g++ handles this, the error message is as follows:

In function ' void foo () [With T = Containsanothertype] ': instantiated to Error:no type named ' iterator ' in ' struct Containsanothertype '

G++ did not find the iterator type in Containsanothertype, so a direct error was found. It did not try to explain it in a different way, and this shows that the g++ is more rigorous and follows the C + + standard. rules for using typename

This last rule looks a bit complicated, and can refer to [Msdn][4]: TypeName is prohibited under the following circumstances:
Outside the template definition, that is, TypeName can only be used in the definition of a template, such as a list of base classes such as the int,vector<int> described earlier, such as template <class c1>: t::i Nnertype cannot precede t::innertype with the TypeName constructor's initialization list if the type is a qualified name that depends on the template parameter, then it must be TypeName before it (unless it is a list of base classes, or in the class's initialization member list In other cases TypeName is optional , that is, for a qualified name that is not a dependent name, the name is optional, such as Vector<int> VI; Other examples

For situations that do not cause ambiguity, you still need to add TypeName to the front, such as:

1 template <class t>
2 void foo () {
3     typename T::iterator iter;
4     //...
5}

Unlike the previous T::iterator * iter may be used as a multiplication expression, there is no ambiguity, but still need to add typename modification.

And look at the following:

1 template <class t>
2 void foo () {
3     typedef typename T::iterator Iterator_type;
4     //...
5}

is somewhat similar to the scalp-numbing code that the article was just beginning with. That's right. Now finally can untie the typename of the fan, see here, I believe you also can explain that line of code, we take another look:

typedef typename __type_traits<t>::has_trivial_destructor Trivial_destructor;

It is clear that the has_trivial_destructor nested type in the __type_traits<t> template class is defined as an alias called Trivial_destructor. See common usage again

Since the TypeName keyword already exists and it can also be used for the most common template parameters, why not abolish the use of class? The answer is also obvious, because before the final standard comes out, all existing books, articles, teaching, and code are all using class, and you can imagine what happens if the standard no longer supports class.

For the use of specifying template parameters, both class and TypeName are supported, but personally I prefer to use typename more, because I never get over class that represents the user-defined type of the hurdle. In addition, semantically, TypeName is more explicit than class. C + + Primer also recommends the use of TypeName:

The

Use Keyword typename instead of the keyword class to specify template type parameters may be more intuitive, after all, you can use built-in types (not class types) as actual type parameters, and typename more clearly indicate that the following name is a type name. However, Keyword TypeName is added to C + + as part of standard C + +, so older programs are more likely to use only the keyword class. Reference C + + Primer effective C + + [a Description of the C + + typename keyword][5] [Wikipedia typename][6] Another history of TypeName, Stan Lippman wrote an article Article [7],stan Lippman who, perhaps you don't know his name, but after reading these you will send out, "Oh, it turns out it was him." ": He is the author of C + + Primer, Inside the C + + Object Model, essential C + +, C # Primer, and he was also the architect of Visual C + +.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.