Подтвердить что ты не робот

Почему ключевое слово "typename" необходимо перед квалифицированными зависимыми именами, а не перед квалифицированными независимыми именами?

class A
{
   static int iterator;
   class iterator
   {
      [...]
   };
   [...]
};

Я (думаю, я) понимаю причину typename здесь:

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

но я не понимаю, почему typename здесь не нужен:

void foo() {
   A::iterator* iter;
   [...]
}

Может ли кто-нибудь объяснить?


EDIT:

Причина, по которой у компилятора нет проблемы с последним, я нашел ответ в комментариях:

в случае A::iterator Я не понимаю, почему компилятор не путал бы его с static int iterator? - xcrypt

@xcrypt, потому что он знает, что оба A::iterator есть и могут выбрать, какой из них зависит от того, как он используется - Seth Carnegie


Причина, по которой компилятор нуждается в typename перед квалифицированными зависимыми именами, на мой взгляд, очень хорошо ответил на принятый ответ Kerrek SB. Не забудьте также прочитать комментарии к этому ответу, особенно этому iammilind:

"T:: A * x; это выражение может быть истинным для обоих случаев, где T:: A - тип, а T:: A - значение. Если A - тип, то это приведет к объявлению указателя, если A - значение, то это приведет к умножению. Таким образом, один шаблон будет иметь разное значение для двух разных типов, что неприемлемо."

4b9b3361

Ответ 1

Имя в С++ может относиться к трем различным уровням сущностей: значениям, типам и шаблонам.

struct Foo
{
    typedef int A;
    static double B;
    template <typename T> struct C;
};

Три имени Foo::A, Foo::B и Foo::C являются примерами всех трех разных уровней.

В приведенном выше примере Foo является полным типом, поэтому компилятор уже знает, что означает Foo::A и т.д. Но теперь представьте это:

template <typename T> struct Bar
{
    T::A x;
};

Теперь мы в беде: что такое T::A? если T = Foo, то T::A = int, который является типом, и все хорошо. Но когда T = struct { char A; };, тогда T::A является значением, что не имеет смысла.

Поэтому компилятор требует, чтобы вы сказали, что должны быть T::A и T::B и T::C. Если вы ничего не говорите, предполагается, что это значение. Если вы скажете typename, это имя типа, и если вы скажете template, это шаблон:

template <typename T> struct Bar
{
    typename T::A x;    // ah, good, decreed typename

    void foo()
    {
        int a = T::B;   // assumed value, OK

        T::template C<int> z;  // decreed template
        z.gobble(a * x);
    }
};

Вторичные проверки, такие как T::B можно конвертировать в int, можно ли умножить a и x, и действительно ли C<int> имеет функцию-член gobble, пока вы фактически не создадите экземпляр шаблон. Но спецификация того, обозначает ли имя значение, тип или шаблон, имеет основополагающее значение для синтаксической корректности кода и должна быть предоставлена ​​прямо там во время определения шаблона.

Ответ 2

Потому что в nontemplate foo вы выполняете действительную операцию умножения (предполагая, что вы объявили iter до этого использования). Попробуйте опустить звезду, и вы получите ошибку компилятора. Класс int скрывает класс.

Ключевое слово typename не мешает этому сокрытию. Только gcc неправильно выполняет это. Поэтому, если вы попытаетесь создать экземпляр своего шаблона функции с помощью A в качестве типа, тогда вы получите ошибку компиляции, так как имя, указанное afteer typenake, будет ссылаться на nontype на любом стандартном компиляторе соответствия.

Ответ 3

Поскольку компилятор не знает, что T::iterator до момента создания шаблона, он не знает, является ли iterator переменной класса, типом, функцией или чем-то. Вы должны сказать, что это тип, используя typename.