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 typedef
should 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 traits
introduction of this technology, and here's __type_traits
a bit like. Just needs had not been met traits
when 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 ++ traits
technology. Here, we forget it, just take it as a normal class, let's explore what this extra typename
is how is it?
A common use of typename
For typename
this 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 typename
changed 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 class
very early already have, why the introduction of typename
this 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 typename
keywords. I remember I had seen similar precautions in a C ++ book in learning C ++, tell us if you use typename
a compiler error, then when it changed class
to.
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 class
has been used for a long period of time). But the more important reason is that, at the time it seems, class
has been completely adequate for their needs this template, therefore, in order to avoid unnecessary trouble, he chose to compromise, reusing existing class
keywords. 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 typename
replaced 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 class
meaning 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, cout
and endl
is a qualified name:
#include <iostream> int main() { std::cout << "Hello world!" << std::endl; }
cout
And endl
preceded std::
, it defines std
the namespace, so called qualified names.
If the above code, with the front using std::cout;
or using namespace std;
, and then use only cout
and endl
, in front of which there is no longer space is defined std::
, so that at this time cout
and endl
is 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, T
, vector<T>
and vector<T>::iterator
called dependency name. The first three definitions is called the non-dependent name.
A little more complicated, if used typedef T U; U u;
, although T
not happen again, but U
still 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::name
calls 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::A
, MyClass::B
, MyClass::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 typename
the mystery.
one example
After Stroustrup drafted the original template specification, people are more carefree use class
for 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 T
of iterator
. There may be a containing iterator
structure type:
struct ContainsAType { struct iterator { /*...*/ }; // ... };
Then like instantiated foo
:
foo<ContainsAType>();
Thus, iter
it is quite clear line, which is a ContainsAType::iterator
type 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 MyClass
is already a complete definition, the compiler of its type can be determined, that is to say MyClass::A
these names for the compiler is also known.
However, if it is like T::iterator
this happen? T
Is 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::iterator
in fact, can be any type of the following three:
- Static data members
- Static member function
- Nested types
In the previous example ContainsAType::iterator
is nested type, there is no problem. But if it is static data members? If an instance of the foo
type of template function like this:
struct ContainsAnotherType { static int iterator; // ... };
Examples of such then foo
the 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 iter
here is not defined, the compiler will complain:
error C2065: ‘iter’ : undeclared identifier
But if iter
is 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 typename
keyword 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::iterator
is not the type of a variable, just use typename
modified:
template <class T> void foo() { typename T::iterator * iter; // ...
So the compiler can determine T::iterator
that 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 ContainsAnotherType
to instantiate foo
, only the former is called a iterator
static 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 typename
tells the compiler iterator
should be a type, but with a defined iterator
structure variables to instantiate a template, the compiler has chosen to ignore the this keyword. An error occurred only because iter
there 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 ++ ContainsAnotherType
is not found in iterator
type, 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::InnerType
can not beT::InnerType
added 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 * iter
may be treated as multiplication expression, there will not be ambiguous, but still need to add typename
modifications.
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 typename
the 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_destructor
nested type definitions called trivial_destructor
alias, clear.
Look at the common usage
Since typename
keyword already exists, and it can also be used to specify the most common template argument, then why not abolish class
the 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 typename
are supported, but personally I tend to use typename
more, because I had not always class
represent user-defined type this bridge. Further, semantically, typename
than class
more 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
- C ++ Primer
- Effective C++
- A Description of the C++ typename keyword
- Wikipedia typename
- Also on the
typename
history, 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 ++. - 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/