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

Когда требуется ключевое слово "typename"?

Возможный дубликат:
Официально, что такое typename для?
Где и почему я должен поместить ключевые слова и ключевые слова для имени?

рассмотрите приведенный ниже код:

template<class K>
class C {
    struct P {};
    vector<P> vec;
    void f();
};

template<class K> void C<K>::f() {
    typename vector<P>::iterator p = vec.begin();
}

Почему ключевое слово "typename" необходимо в этом примере? Существуют ли другие случаи, когда необходимо указать "typename"?

4b9b3361

Ответ 1

Короткий ответ: всякий раз, когда ссылается на вложенное имя, которое является зависимым именем, т.е. вложенным внутри экземпляра шаблона с неизвестным параметром.

Длинный ответ: в С++ есть три уровня сущностей: значения, типы и шаблоны. Все они могут иметь имена, и только одно имя не говорит вам, какой уровень сущности он есть. Скорее, информация о природе объекта имени должна быть выведена из контекста.

Всякий раз, когда этот вывод невозможен, вы должны указать его:

template <typename> struct Magic; // defined somewhere else

template <typename T> struct A
{
  static const int value = Magic<T>::gnarl; // assumed "value"

  typedef typename Magic<T>::brugh my_type; // decreed "type"
  //      ^^^^^^^^

  void foo() {
    Magic<T>::template kwpq<T>(1, 'a', .5); // decreed "template"
    //        ^^^^^^^^
  }
};

Здесь должны были быть указаны имена Magic<T>::gnarl, Magic<T>::brugh и Magic<T>::kwpq, потому что невозможно сказать: поскольку Magic является шаблоном, сама природа типа Magic<T> зависит от T - могут быть, например, специализации, которые полностью отличаются от основного шаблона.

Что делает Magic<T>::gnarl зависимым именем тот факт, что мы находимся внутри определения шаблона, где T неизвестно. Если бы мы использовали Magic<int>, это было бы иначе, поскольку компилятор знает (вы обещаете!) Полное определение Magic<int>.

(Если вы хотите проверить это самостоятельно, вот примерное определение Magic, которое вы можете использовать. Простите использование constexpr для специализации для краткости, если у вас старый компилятор, не стесняйтесь изменять статическое членное объявление константы в старом стиле pre-С++ 11.)

template <typename T> struct Magic
{
  static const T                    gnarl;
  typedef T &                       brugh;
  template <typename S> static void kwpq(int, char, double) { T x; }
};
template <> struct Magic<signed char>
{
  // note that `gnarl` is absent
  static constexpr long double brugh = 0.25;  // `brugh` is now a value
  template <typename S> static int kwpq(int a, int b) { return a + b; }
};

Использование:

int main()
{
  A<int> a;
  a.foo();

  return Magic<signed char>::kwpq<float>(2, 3);  // no disambiguation here!
}

Ответ 2

Ключевое слово typename необходимо, потому что iterator является зависимым типом на P. Компилятор не может догадаться, если iterator ссылается на значение или тип, поэтому он принимает его значение, если вы не оверите typename. Это необходимо всякий раз, когда существует тип, зависящий от аргумента шаблона, в контексте, который будет иметь либо типы, либо значения. Например, поскольку базовые классы typename не нужны, поскольку базовый класс должен быть типом.

По этому же вопросу существует ключевое слово template, которое позволяет компилятору узнать, что какое-то зависимое имя является функцией шаблона вместо значения.

Ответ 3

Ключевое слово typename необходимо всякий раз, когда имя типа зависит от параметра шаблона (поэтому компилятор может "знать" семантику идентификатора (типа или значения) без полной таблицы символов на первом проходе).


Не в том же значении и немного менее распространенное ключевое слово lone typename также может быть полезно при использовании общих параметров шаблона: http://ideone.com/amImX

#include <string>
#include <list>
#include <vector>

template <template <typename, typename> class Container,
          template <typename> class Alloc = std::allocator>
struct ContainerTests 
{
    typedef Container<int, Alloc<int> > IntContainer;
    typedef Container<std::string, Alloc<int> > StringContainer;
    //
    void DoTests()
    {
        IntContainer ints;
        StringContainer strings;
        // ... etc
    }
};

int main()
{
    ContainerTests<std::vector> t1;
    ContainerTests<std::list>   t2;

    t1.DoTests();
    t2.DoTests();
}