ChatGPT解决这个技术问题 Extra ChatGPT

Officially, what is typename for?

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?


N
NAND

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 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.


Great book. Read it through once then keep it as a reference if you like.
The astute reader will realize that a multiplication expression is not allowed by the grammar for a member declaration. As such, C++20 dispenses with the need for this typename (though not all of them!).
Didn't convince me. Once the template is being instantiated, it is very well defined what is the T::Subtype
M
Mansuro

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


Yes, but then why was a new keyword typename necessary, if you could use the existing keyword class for the same purpose?
@Jesper: I think Xenus' answer is confusing here. typename became necessary to fix the parsing issue as described in Naveen's answer by quoting Josuttis. (I don't think inserting a class at this place would have worked.) Only after the new keyword was accepted for this case, it was also allowed in template argument declarations (or is that definitions?), because that class there has always been somewhat misleading.
m
moonshadow

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!
    .
    .

A
AnT stands with Russia

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.


See also A Description of the C++ typename keyword for more (background) information.
p
phlipsy

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 Ts. 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.)


N
NAND

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 class X // [1] { typename T::Y _member; // [2] }


G
Gupta

I think all of the answers have mentioned that the typename keyword, is used in two different cases:

a) When declaring a template type parameter. e.g.

template<class T> class MyClass{};        // these two cases are
template<typename T> class MyNewClass{};  // exactly the same.

Which there is no difference between them and they are EXACTLY the same.

b) Before using a nested dependent type name for a template.

template<class T>
void foo(const T & param)
{
   typename T::NestedType * value; // we should use typename here
}

Which not using typename leads to parsing/compilation errors.

What I want to add to the second case, as mentioned in Scot Meyers book Effective C++, is that there is an exception of using typename before a nested dependant type name. The exception is that if you use the nested dependant type name either as a base class or in a member initialization list, you should not use typename there:

template<class T>
class D : public B<T>::NestedType               // No need for typename here
{
public:
   D(std::string str) : B<T>::NestedType(str)   // No need for typename here
   {
      typename B<T>::AnotherNestedType * x;     // typename is needed here
   }
}

Note: Using typename for the second case (i.e. before nested dependent type name) is not needed since C++20.


J
Jobin
#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;
}