(Turn) to know the origin and use of C ++ typename of the infinite

Hou Jie in Effective C ++ version of Chinese Yi Xu mentioned in:

C ++ is difficult to learn, but also because it offers four different (but complementary) programming mode of thinking: procedural-based, object-based, object-oriented, generics

For me the last few use generic programming, the programming basically stuck front among three kinds of thinking. Although not a glimpse of advanced technology and modern generics, but the first three have been thinking almost meet all the needs I have, so this had not been in-depth to understand generic programming.

Due

Recently, to see this line of code:

typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;

Although there have been many years of C ++ experience, but above this short line of code but I see scalp tingling. Looks like it should be the definition of a type alias, but typedefshould not be using it like this, typedef+ original new type name + type name:

typedef char* PCHAR;

Why be here one more typename? In addition __type_traits, what is? Looks vaguely familiar, think before the Effective C ++ have seen traitsintroduction of this technology, and here's __type_traitsa bit like. Just needs had not been met traitswhen it was not carefully studied. However, a large number of STL filled with a variety of traits, an investigation found out that it is a very advanced technology, which is very common in the more modern high-level languages. So this took some time to learn it, then there will be another article to detail the C ++ traitstechnology. Here, we forget it, just take it as a normal class, let's explore what this extra typenameis how is it?

A common use of typename

For typenamethis keyword, if you are familiar with C ++ templates, you will know that it has a most common usage (excerpt 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) return -1;
    if (v2 < v1) return 1;
    return 0;
}

You might think the above code is typenamechanged class, too, can, yes! This will have a question, what differentiated these two approaches? After reviewing C ++ Primer, we found that the two are completely different. So why C ++ supports two ways to do? Since the classvery early already have, why the introduction of typenamethis key it? Good question, and there are some little-known history (maybe I just do not know :-)). With these questions, we began to explore the journey.

The source typename

For some friends earlier contact with C ++, you may know, at the time of the C ++ standard has not been uniform, many older compilers support only class, because then C ++ and no typenamekeywords. I remember I had seen similar precautions in a C ++ book in learning C ++, tell us if you use typenamea compiler error, then when it changed classto.

Everything comes down to history.

Stroustrup in the initial drafting template specification, he took into account the introduction of a new keyword template type parameter, but doing so may destroy many programs have been written (as classhas been used for a long period of time). But the more important reason is that, at the time it seems, classhas been completely adequate for their needs this template, therefore, in order to avoid unnecessary trouble, he chose to compromise, reusing existing classkeywords. So just before the ISO C ++ standard came out, wanted template type parameter is only one way to specify, and that is to use class. This also explains why many older compilers support only class.

But for many people, always accustomed to class, because its purpose as it exists, is to distinguish it from the built-in type of language used to declare a user-defined type. So the definition of the following template function (with respect to the embodiment, only typenamereplaced class):

template <class T>
int compare(const T &v1, const T &v2)
{
    if (v1 < v2) return -1;
    if (v2 < v1) return 1;
    return 0;
}

On the surface it looks as if the parameter template should only support user-defined types, so use the built-in type or language pointer to call the template function when always feel a hint of strange (although no errors):

int v1 = 1, v2 = 2;
int ret = compare(v1, v2);

int *pv1 = NULL, *pv2 = NULL;
ret = compare(pv1, pv2);

The reason surprising that the classmeaning of performance in class and template there appears to be some inconsistencies, the former for user-defined types, which includes built-in types and language pointer. Precisely because of this, people seem to feel that time does not introduce a new keyword might be a mistake.

This is a factor in the introduction of new standards committee keywords, but in fact there is another more important reason, and articles on the most relevant line of code to start.

Some of the key concepts

Before we unveil the real reason, first to keep a little mystery, because in order to better understand the C ++ standard, there are several important concept to introduce in advance.

Defining a name and unqualified name

Qualified name (qualified name), name suggests, is defining the name of the namespace. Look at the following code, coutand endlis a qualified name:

#include <iostream>

int main()  {
    std::cout << "Hello world!" << std::endl;
}

coutAnd endlpreceded std::, it defines stdthe namespace, so called qualified names.

If the above code, with the front using std::cout;or using namespace std;, and then use only coutand endl, in front of which there is no longer space is defined std::, so that at this time coutand endlis called a non-qualified name (unqualified name).

Dependent and non-dependent name names

Dependent name (dependent name) refers to the name of the template depends on the parameters, rather than relying on the name of (non-dependent name), by contrast, means that do not depend on a template parameter name. Look at the following code:

template <class T>
class MyClass {
    int i;
    vector<int> vi;
    vector<int>::iterator vitr;

    T t;
    vector<T> vt;
    vector<T>::iterator viter;
};

Because it is built-in types, so the first three classes defined types have been known at the time of this statement template class. However, for the next three lines of definition, only know their types when the template is instantiated, because they are dependent on a template parameter T. Therefore, Tvector<T>and vector<T>::iteratorcalled dependency name. The first three definitions is called the non-dependent name.

A little more complicated, if used typedef T U; U u;, although Tnot happen again, but Ustill rely name. Thus, whether direct or indirect, long dependent on a template parameter, the name is dependent on the name.

Class scope

When the class name of the external access class, the class scope operator may be used, the form MyClass::namecalls usually there are three: static data members and member functions static nested types:

struct MyClass {
    static int A;
    static int B();
    typedef int C;
}

MyClass::AMyClass::BMyClass::C Correspond to the above three types.

The real reason for the introduction of typename

The end of the three concepts discussed above, then let us uncover typenamethe mystery.

one example

After Stroustrup drafted the original template specification, people are more carefree use classfor a long time. However, with the advent of C ++ standardization work, it was discovered that a template definition:

template <class T>
void foo() {
    T::iterator * iter;
    // ...
}

What is the purpose of this code is? Most people first reaction might be: The authors would like to define a pointer iter, it points to the type of class is included in the scope Tof iterator. There may be a containing iteratorstructure type:

struct ContainsAType {
    struct iterator { /*...*/ };
    // ...
};

Then like instantiated foo:

foo<ContainsAType>();

Thus, iterit is quite clear line, which is a ContainsAType::iteratortype of pointer. So far, I guess that's right, everything looks very good.

Problem surfaced

In the scope of a class, we introduced three names, as MyClassis already a complete definition, the compiler of its type can be determined, that is to say MyClass::Athese names for the compiler is also known.

However, if it is like T::iteratorthis happen? TIs a type parameter template, it is only when the template is instantiated wait will know what type, not to mention the internal iterator. By introducing a scope in front of the class, we can know, T::iteratorin fact, can be any type of the following three:

  • Static data members
  • Static member function
  • Nested types

In the previous example ContainsAType::iteratoris nested type, there is no problem. But if it is static data members? If an instance of the footype of template function like this:

struct ContainsAnotherType {
    static int iterator;
    // ...
};

Examples of such then foothe parameter type:

foo<ContainsAnotherType>();

So, T::iterator * iter;it is compiled into an instance ContainsAnotherType::iterator * iter;, what is this? In front of a static member variable rather than a type, then it became a multiplication expression, but iterhere is not defined, the compiler will complain:

error C2065: ‘iter’ : undeclared identifier

But if iteris a global variable, then this line of code exactly right, which is calculated by multiplying the number two expressions, the return value is discarded.

The same line of code can be in two completely different ways to explain, but before the template is instantiated, there is no way to distinguish between them, this is definitely a hotbed for the breeding of various bug. Then C ++ standards committee no longer bear it, in order to know which way in the end choose to interpret the code above when its instantiated, the Commission decided to introduce a new keyword, it is typename.

Awaited

Let's look at the C ++ standard :

A name used in a template declaration or definition and that is dependent on a template-parameter is 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 dependent as defined in the name of the template template parameter, only the existence of this type in the name of the instance parameters, or the name before using a typenamekeyword to modify, the compiler will type the name as is. In addition to the above two cases, it will not be treated as a type.

So, if you want to tell the compiler T::iteratoris not the type of a variable, just use typenamemodified:

template <class T>
void foo() {
    typename T::iterator * iter;
    // ...

So the compiler can determine T::iteratorthat a type, and no longer need to wait until instantiated to determine the period, thus eliminating the ambiguities mentioned earlier.

Different compilers handle error conditions

But if you still use ContainsAnotherTypeto instantiate foo, only the former is called a iteratorstatic member variables, while the latter is needed is a type, what happens? My experiments were made in 2010 ++ and g ++ 4.3.4 Visual C, results are as follows:

Visual C ++ 2010 and still report the error as before:

error C2065: ‘iter’ : undeclared identifier

Although we have used the keyword typenametells the compiler iteratorshould be a type, but with a defined iteratorstructure variables to instantiate a template, the compiler has chosen to ignore the this keyword. An error occurred only because iterthere is no definition.

Let's look at how g ++ to handle this situation, it's the following error message:

In function ‘void foo() [with T = ContainsAnotherType]’: instantiated from here error: no type named ‘iterator’ in ‘struct ContainsAnotherType’

In g ++ ContainsAnotherTypeis not found in iteratortype, so a direct error. It does not try to explain in another way, we can see, at this point, g ++ more stringent, more follow the C ++ standard.

Typename of usage rules

This last rule may seem complicated, can refer to the MSDN :

  • typename prohibited under the following circumstances:
    • Outside the template definition, that is typename can only be used to define the template
    • Non-limiting type, such as described earlier int, vector<int>and the like
    • List of base classes, such template <class T> class C1 : T::InnerTypecan not be T::InnerTypeadded to the front typename
    • Initializer list constructor
  • If the type is dependent on the defined name template parameters, then it must be added before typename (unless it is the base class list, or members of the class to initialize the list)
  • In other cases typename is optional, that is not dependent name for a defined name, the name is optional, e.g.vector<int> vi;

Other examples

In the case of not ambiguous, still need to be preceded typename, such as:

template <class T>
void foo() {
    typename T::iterator iter;
    // ...
}

Unlike previous T::iterator * itermay be treated as multiplication expression, there will not be ambiguous, but still need to add typenamemodifications.

Look at this following:

template <class T>
void foo() {
    typedef typename T::iterator iterator_type;
    // ...
}

And whether that article boggling array of lines of code beginning slightly similar? Yes! Now he can finally solved typenamethe mystery, and see here, I'm sure you will be able to explain the line of code, we take another look:

typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
It is __type_traits<T>this class template has_trivial_destructornested type definitions called trivial_destructoralias, clear.

Look at the common usage

Since typenamekeyword already exists, and it can also be used to specify the most common template argument, then why not abolish classthe use of it? The answer is very obvious, because before the final standard out, all existing books, articles, teaching, the code is using class, you can imagine, if the standard is no longer supported class, what would happen.

For a given template parameters of this usage, though class, and typenameare supported, but personally I tend to use typenamemore, because I had not always classrepresent user-defined type this bridge. Further, semantically, typenamethan classmore clearly expressed. C ++ Primer is also recommended typename:

Use the keyword typename instead of class keyword specifies the template type parameter may be more intuitive, after all, you can use the built-in types (non-class type) as the actual type parameter, and, typename identify more clearly behind the name is a type name . However, the keyword typename was added as part of the standard C ++ to C ++, and so the old program are more likely to use only the keyword class.

reference

  1. C ++ Primer
  2. Effective C++
  3. A Description of the C++ typename keyword
  4. Wikipedia typename
  5. Also on the typenamehistory, Stan Lippman wrote an article , Stan Lippman sort of person, maybe you do not know his name, but you'll be sure to read these, "Oh, is he!": He is the  C ++ Primer, Inside the C ++ Object Model, Essential C ++, C # Primer  author of books etc. in addition, he was also the architect for Visual C ++.
  6. In StackOverflow has a very in-depth answer on, thanks @Emer provide this link in this review.

At the end of write

A simple keyword has been full of twists and turns, which can reflect the development process of a language from one angle, how many decisions, and compromise to go through twists and turns, it finally developed into the present form. In a certain period, due to various factors of history, technology, ideas and so on, the design will always make certain concessions to reality, some "imperfect" design, in order to maintain backward compatibility, some "not perfect" historical factors have been retained. Now I can understand the oft-criticized Windows operating system, Intel chip, IE browser, Visual C ++, etc., in order to maintain backward compatibility, these had to remain "imperfect" in the new design, although brought more many good features, but there are always some people because they are cast aside these historical factors, but also for their own had the same move and ashamed. But it is these "imperfect" appears, only to get people to pay more attention in the design of the follow-up, standing on the shoulders of our predecessors, to make better, more well-designed, so the technology has continually move forward.

However, there are some examples of bold and daring to try, such as C ++ 11, it changed greatly even Stroustrup says it's like a new language . For more than 30 years of history with the "old" language, not only has not been defeated various upstart, but continues to learn from the younger generation who absorbed some good features, old and solid, it is not easy. There Python 3, in order to clean up some of the issues of grammar 2.x version, breaking backward compatibility with version 2.x, this sacrifice in exchange for backward compatibility progressive approach is certainly delayed the time to accept the new version of but I believe this is the throes of forward progress. Guido van Rossum of new channels such courage is really quite impressive, as this approach could eventually accepted by the people, to test everything to history.

Transfer: http://feihu.me/blog/2014/the-origin-and-usage-of-typename/

Guess you like

Origin www.cnblogs.com/wangzxblog/p/11887780.html