2009-08-08 3 views
10

Я читал статью Википедии о SFINAE и встречается следующий пример кода:Почему вам иногда приходится писать `typename T` вместо просто` T`?

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» вместо просто «Т». Тем не менее, было бы неплохо узнать фактическую логику этого. Кто-нибудь должен объяснить?

+1

Рекомендую ознакомиться с шаблоном faq: http://womble.decadentplace.org.uk/c++/template-faq.html –

ответ

8

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

+1

Вы имеете в виду «обычно, когда задействованы параметры шаблона» (в этом случае я уверен, что это только * при использовании параметров шаблона), или это опечатка для «, которая характерна для параметров шаблона» ? –

+1

@onebyone, хороший улов, я действительно имел в виду «типичный», а не «типично», отредактировал мой ответ на fix, tx. –

13

Короткий вариант, который вам нужно сделать 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>().

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

+0

jalf, возможно, стоит упомянуть альтернативный смысл 'T :: something' (статические данные, имя функции). И как только вы на это так хорошо, возможно, стоит добавить объяснение, почему иногда нам нужно также вводить «шаблон». У этого по той же причине, поэтому это не займет много времени. – sbi

+0

«шаблон» - это не одно и то же рассуждение. Он не имеет ничего общего с зависимыми именами. Он относится к категории «проблем, которые, как правило, вызывают вас с помощью шаблонов», но я не думаю, что это та же самая причина, что и в «typename». – jalf

+1

@jalf: Разве это не касается имен шаблонов? В любом случае, причины довольно аналогичны. Это разрешено только в коде шаблона, и его нужно использовать для различения зависимых имен членов, которые являются именами шаблонов из зависимых имен членов, которых нет. –

3

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

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

Для определения № 2 typename T не требуется , который является типом как часть определения шаблона.

Смежные вопросы