Почему вам иногда нужно написать `typename T` вместо просто `T`?

StackOverflow https://stackoverflow.com/questions/1247555

  •  12-09-2019
  •  | 
  •  

Вопрос

Я читал статью в Википедии о СФИНАЭ и столкнулся со следующим примером кода:

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

Это было полезно?

Решение

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

Другие советы

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top