Почему вам иногда нужно писать `typename T` вместо просто` T`? - PullRequest
10 голосов
/ 08 августа 2009

Я читал статью в Википедии на SFINAE и обнаружил следующий пример кода:

struct Test 
{
    typedef int Type;
};

template < typename T > 
void f( typename T::Type ) {} // definition #1

template < typename T > 
void f( T ) {}                // definition #2

void foo()
{
    f< Test > ( 10 ); //call #1 

    f< int > ( 10 );  //call #2 without error thanks to SFINAE
}

Теперь я на самом деле написал код, подобный этому, и каким-то интуитивным образом я знал, что мне нужно набирать «typename T» вместо просто «T». Однако было бы неплохо узнать реальную логику этого. Кто-нибудь хочет объяснить?

Ответы [ 3 ]

13 голосов
/ 08 августа 2009

Короткая версия, которую вам нужно сделать typename X::Y, когда X равен или зависит от параметра шаблона. Пока X не известен, компилятор не может определить, является ли Y типом или значением. Поэтому вы должны добавить typename, чтобы указать, что это тип.

Например:

template <typename T>
struct Foo {
  typename T::some_type x; // T is a template parameter. `some_type` may or may not exist depending on what type T is.
};

template <typename T>
struct Foo {
  typename some_template<T>::some_type x; // `some_template` may or may not have a `some_type` member, depending on which specialization is used when it is instantiated for type `T`
};

Как указывает sbi в комментариях, причина неоднозначности заключается в том, что Y может быть статическим членом, перечислением или функцией. Не зная типа X, мы не можем сказать. Стандарт определяет, что компилятор должен предполагать, что это значение, если оно явно не помечено как тип с помощью ключевого слова typename.

И, похоже, комментаторы действительно хотят, чтобы я упомянул и еще один связанный случай:;)

Если зависимое имя является шаблоном члена функции, и вы вызываете его с явным аргументом шаблона (например, foo.bar<int>()), вам необходимо добавить ключевое слово template перед именем функции, как в foo.template bar<int>() .

Причина этого в том, что без ключевого слова template компилятор предполагает, что bar является значением, и вы хотите вызвать для него оператор меньше чем (operator<).

8 голосов
/ 08 августа 2009

В общем, синтаксис C ++ (унаследованный от C) имеет технический дефект: синтаксический анализатор ДОЛЖЕН знать, называет ли тип тип или нет, в противном случае он просто не может разрешить определенные неоднозначности (например, является ли X * Y умножением, или объявление указателя Y на объекты типа X - все зависит от того, назовет ли X тип ...! -). typename «прилагательное» позволяет вам сделать это совершенно ясным и явным, когда это необходимо (что, как уже упоминалось в другом ответе, типично при использовании параметров шаблона; -).

3 голосов
/ 08 августа 2009

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

В вашем примере вы используете typename T::Type в определении # 1, поскольку T::Type зависит от параметра шаблона T и в противном случае может быть элементом данных.

Вам не нужно typename T в определении # 2, так как T объявлен как тип как часть определения шаблона.

...