Когда необходимо ключевое слово "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"?

3 ответа

Решение

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

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

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

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 в специализации на краткость; если у вас есть старый компилятор, не стесняйтесь изменить объявление константы статического члена на старый стиль до C++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!
}

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

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

Ключевое слово 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();
}
Другие вопросы по тегам