2

In the following example:

template<typename T>
struct MyList {
    typedef std::list<T> type1;
};

template<typename T>
class MyList2 {
    typename MyList<T>::type1 type2;
};

I thought that both type1 and type2 are dependent name since both of their types depend on the template parameter. However, why is the first one considered non-dependent since I can use typedef with it?

TYeung
  • 2,579
  • 2
  • 15
  • 30
  • 1
    In the first case, a compiler knows that `T` is a type and that `std::list` is a class template that takes a type template parameter. There is no ambiguity. In the second case, a compiler doesn't know what `::type1` refers to. It could be a type, it could be a static data member. That's why you need `typename` only in the second case. – Evg Jun 03 '23 at 14:04
  • 2
    BTW, both `type1` and `type2` are dependent name :) – Jarod42 Jun 03 '23 at 14:09
  • Your question is probably not what you intended to ask. It seems that you intent to ask why `typename` is required for the second part, but not the first. That's not related to whether or not `type1` and `type2` are dependent names. – user17732522 Jun 03 '23 at 14:22
  • @user17732522 Yes, I check the explanations on cppreference again. Although both `std::list` and `MyList::type1` are both dependent on T, the former is for sure a type while the second is not. So the key should be whether the name is already established as a type. Is that correct? – TYeung Jun 03 '23 at 14:52
  • This depends on context, too. `typename` should not be used if a dependent type is specified as a base class. Starting from C++20, `typename` [becomes optional](https://stackoverflow.com/questions/61990971/why-dont-i-need-to-specify-typename-before-a-dependent-type-in-c20) in other contexts where only a type can appear. – Evg Jun 03 '23 at 17:27
  • @Evg Can you elaborate on `typename` should not be used if a dependent type is specified as a base class? – TYeung Jun 04 '23 at 12:50
  • 1
    `template class Derived : public BaseSelector::Type { ... };` – Evg Jun 04 '23 at 13:33

2 Answers2

6

A dependent name is, in C++ terms, a name whose grammatical properties are dependent on a template parameter.

Names can mean a lot of different things in C++, and the grammatical location where a name can be used depends on certain properties of that name. In order to understand what X x; is trying to do, the compiler needs to know what X is at this point in the program. If X names a type, then this is declaring a variable named x. The compiler doesn't need to know everything about X, but grammatically, in order to even begin to make sense of this text, the compiler does need to know that X here names a type.

std::list is the name of a class template; the compiler can easily see that because that's how it is declared. It doesn't need to have the body of the declaration; template<typename T> class list; is sufficient. std::list<T> is an instantiation of a class template, so just from the declaration, the compiler knows that this is the name of a type.

However, MyList<T>::type1 now requires more than just the declaration of the MyList template. It requires actually instantiating that template. And if T is itself a template parameter of the current code (as is the case for MyList2), then instantiation at the point of initially compiling the code is impossible. That instantiation must be delayed until MyList2 is itself given a concrete T to work with.

But the compiler still has to make sense of this code: typename MyList<T>::type1 type2;. And to do that, it needs some idea of what MyList<T>::type1 actually is. Without instantiating MyList<T>.

Which is why you have to tell it that it is a typename.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
1

While std::list<T> uses T, it does not depend on T. The name std::list exists as long as <list> is included.

On the other hand typename MyList<T>::type1 type2; does depend on T as there is no way to know what type1 is without instantiating MyList<T>. That is why you have to use typename as the grammar by default will consider type1 to be a member variable or member function, not a type.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402