可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
On occasion I\'ve seen some really indecipherable error messages spit out by gcc when using templates... Specifically, I\'ve had problems where seemingly correct declarations were causing very strange compile errors that magically went away by prefixing the \"typename\" keyword to the beginning of the declaration... (For example, just last week, I was declaring two iterators as members of another templated class and I had to do this)...
What\'s the story on typename?
回答1:
Following is the quote from Josuttis book:
The keyword typename was introduced to
specify that the identifier that
follows is a type. Consider the
following example:
template <class T>
Class MyClass
{
typename T::SubType * ptr;
...
};
Here, typename is used to clarify that
SubType is a type of class T. Thus,
ptr is a pointer to the type
T::SubType. Without typename, SubType
would be considered a static member.
Thus
T::SubType * ptr
would be a multiplication of value
SubType of type T with ptr.
回答2:
Stan Lippman\'s BLog post suggests :-
Stroustrup reused the existing class
keyword to specify a type parameter
rather than introduce a new keyword
that might of course break existing
programs. It wasn\'t that a new keyword
wasn\'t considered -- just that it
wasn\'t considered necessary given its
potential disruption. And up until the
ISO-C++ standard, this was the only
way to declare a type parameter.
So basically Stroustrup reused class keyword without introducing a new keyword which is changed afterwards in the standard for the following reasons
As the example given
template <class T>
class Demonstration {
public:
void method() {
T::A *aObj; // oops …
// …
};
language grammar misinterprets T::A *aObj;
as an arithmetic expression so a new keyword is introduced called typename
typename T::A* a6;
it instructs the compiler to treat the subsequent statement as a declaration.
Since the keyword was on the payroll,
heck, why not fix the confusion caused
by the original decision to reuse the
class keyword.
Thats why we have both
You can have a look at this post, it will definitely help you, I just extracted from it as much as I could
回答3:
Consider the code
template<class T> somefunction( T * arg )
{
T::sometype x; // broken
.
.
Unfortunately, the compiler is not required to be psychic, and doesn\'t know whether T::sometype will end up referring to a type name or a static member of T. So, one uses typename
to tell it:
template<class T> somefunction( T * arg )
{
typename T::sometype x; // works!
.
.
回答4:
In some situations where you refer to a member of so called dependent type (meaning \"dependent on template parameter\"), the compiler cannot always unambiguously deduce the semantic meaning of the resultant construct, because it doesn\'t know what kind of name that is (i.e. whether it is a name of a type, a name of a data member or name of something else). In cases like that you have to disambiguate the situation by explicitly telling the compiler that the name belongs to a typename defined as a member of that dependent type.
For example
template <class T> struct S {
typename T::type i;
};
In this example the keyword typename
in necessary for the code to compile.
The same thing happens when you want to refer to a template member of dependent type, i.e. to a name that designates a template. You also have to help the compiler by using the keyword template
, although it is placed differently
template <class T> struct S {
T::template ptr<int> p;
};
In some cases it might be necessary to use both
template <class T> struct S {
typename T::template ptr<int>::type i;
};
(if I got the syntax correctly).
Of course, another role of the keyword typename
is to be used in template parameter declarations.
回答5:
Two uses:
- As a template argument keyword (instead of \'class\')
- A typename keyword tells the compiler that an identifier is a type (rather than a static member variable)
template <typename T> class X // [1]
{
typename T::Y _member; // [2]
}
回答6:
The secret lies in the fact that a template can be specialized for some types. This means it also can define the interface completely different for several types. For example you can write:
template<typename T>
struct test {
typedef T* ptr;
};
template<> // complete specialization
struct test<int> { // for the case T is int
T* ptr;
};
One might ask why is this useful and indeed: That really looks useless. But take in mind that for example std::vector<bool>
the reference
type looks completely different than for other T
s. Admittedly it doesn\'t change the kind of reference
from a type to something different but nevertheless it could happen.
Now what happens if you write your own templates using this test
template. Something like this
template<typename T>
void print(T& x) {
test<T>::ptr p = &x;
std::cout << *p << std::endl;
}
it seems to be ok for you because you expect that test<T>::ptr
is a type. But the compiler doesn\'t know and in deed he is even advised by the standard to expect the opposite, test<T>::ptr
isn\'t a type. To tell the compiler what you expect you have to add a typename
before. The correct template looks like this
template<typename T>
void print(T& x) {
typename test<T>::ptr p = &x;
std::cout << *p << std::endl;
}
Bottom line: You have to add typename
before whenever you use a nested type of a template in your templates. (Of course only if a template parameter of your template is used for that inner template.)
回答7:
#include <iostream>
class A {
public:
typedef int my_t;
};
template <class T>
class B {
public:
// T::my_t *ptr; // It will produce compilation error
typename T::my_t *ptr; // It will output 5
};
int main() {
B<A> b;
int my_int = 5;
b.ptr = &my_int;
std::cout << *b.ptr;
std::cin.ignore();
return 0;
}