Когда я должен использовать имя типа ключевого слова при использовании шаблонов

Недавно я работал над небольшим проектом, и я не мог кое-что понять.

Мне дали файл .h, содержащий класс с использованием шаблона имени типа. Внутри этого класса был частный класс.

template <typename T>
class Something
{
public:
        Something();
        ~Something();

        Node* Function1(int index);
        int Index(const T& id);


private:
        class Node()
        {
                public:
                T id;

                //Imagine the rest for the Node


        };      
};

Проблема возникла, когда я хотел определить функции класса «Что-то».

Вот как я это делал (в файле .inl)

template<typename T>
Node* Something::Function1(int index) //Is the return type well written?
{
        // returns the node at the specified index
}

template<typename T>
int Something::Index(const T& id) //Is the parameter type well specified?
{
        // returns the index of the node with the specified id
}

Итак, часть ошибок была в части определений... Должен ли я сообщать компилятору, что возвращаемый тип (в данном случае Node*) использует шаблон имени типа (например: typename Node*)? А что с параметром? typename const Node& ?

Итак, когда мне нужно указать, использует ли функция/параметр шаблон?

Спасибо за ваше время.


person Pacane    schedule 19.11.2010    source источник
comment
Пожалуйста, разместите sinppets в своем вопросе!   -  person mmmmmmmm    schedule 19.11.2010
comment
Я разместил ссылки с фрагментами. Я не мог понять, как правильно делать отступы в своих фрагментах.   -  person Pacane    schedule 19.11.2010


Ответы (4)


Для Function1 вам нужно сообщить компилятору, что такое Node — в данном случае это вложенный тип внутри Something<T>. Поскольку он зависит от T (это зависимое имя), вам нужно сообщить компилятору, что это тип, поэтому вы должны записать его как typename Something<T>::Node. Проблема в том, что могут быть некоторые T, для которых Something<T>::Node на самом деле не является типом (т. е. если вы частично специализируетесь на Something<T>).

Для Index то, что у вас есть, подходит — const T& — это просто ссылка на const T, а компилятор знает, что такое T.

person Stuart Golodetz    schedule 19.11.2010
comment
поэтому, если бы параметр был указателем на узел, мне пришлось бы снова указать часть имени типа, верно? - person Pacane; 19.11.2010
comment
Поскольку вы находитесь в пределах Something<T>, вы можете просто написать Node *. Но если бы вы написали это явно, вам нужно было бы сказать typename Something<T>::Node *, а не просто Something<T>::Node *. - person Stuart Golodetz; 19.11.2010
comment
Спасибо за объяснение и за то, что уделили время. - person Pacane; 19.11.2010

Простое правило: вам нужно использовать ключевое слово typename каждый раз, когда вы называете тип, используя синтаксис Class::Type, если часть Class зависит от параметра шаблона. (Часть Class может быть параметром шаблона, или это может быть typedef в шаблоне вашего класса и т. д.)

Изменить: есть также некоторая путаница в правилах области видимости вложенных классов. Это в основном не зависит от проблемы typename, поэтому вот пример без шаблона.

class Outer {
public:
  class Inner {
  };
  Inner* func(Inner* obj);
};

Outer::Inner* func(Inner* obj)
{
}

Полное имя InnerOuter::Inner. Но вы также можете использовать более короткое имя Inner в любом месте из области видимости класса Outer, включая все объявления класса func. В определении func тип возвращаемого значения НЕ входит в область действия Outer, поэтому необходимо полное имя. Но после ( параметры функции НАХОДЯТСЯ в области действия Outer, поэтому короткое имя подходит.

В сочетании с шаблонностью исходного примера, поскольку эквивалентом Outer является Something<T>, вам нужно ключевое слово typename, чтобы сказать Something<T>::Node.

person aschepler    schedule 19.11.2010
comment
поэтому тип параметра в определении индекса функции должен быть записан typename const Class‹T›::Node& id ? - person Pacane; 19.11.2010
comment
@Pacane: Это зависит от того, хотите ли вы передать T или Node? Если вы хотите передать Node, полная версия будет работать, но она не нужна, так как компилятор только что увидел, что вы определяете метод Something, и const Node& подойдет. - person aschepler; 19.11.2010
comment
Спасибо за объяснение и за то, что уделили время. - person Pacane; 19.11.2010
comment
Отредактировано с объяснением области действия класса в определениях членов. - person aschepler; 19.11.2010

typename и class эквивалентны в списке параметров типа шаблона:

template <class T> class C;

такой же как

template <typename T> class C;

typename требуется при ссылке на зависимые имена :

template <typename T> struct A {
    typedef typename T::some_type container;
};
person Nikolai Fetissov    schedule 19.11.2010
comment
На самом деле имя типа и класс НЕ эквивалентны в списке параметров. Ключевое слово typename не может заменить «класс», когда ожидаются параметры шаблона шаблона. - person Edward Strange; 19.11.2010
comment
Да, спасибо, забыл об этом. Хотя это просто пиздец со стороны языка - они просто забыли об этом случае, как и я здесь :) - person Nikolai Fetissov; 19.11.2010

person    schedule
comment
Хорошо, а как насчет типа параметра другого метода? - person Pacane; 19.11.2010
comment
Node — имя вложенного класса. Таким образом, вы не можете использовать его, если вы не находитесь в коде для метода Something<T> или используете полное имя typename Something<T>::Node. И тип возвращаемого значения метода (определенный вне класса) еще не находится в методе Something, так что вы должны сделать это понятным для компилятора. Другой метод не будет иметь такой проблемы, потому что компилятор всегда знает, что означает int. - person aschepler; 19.11.2010
comment
Спасибо за объяснение и за то, что уделили время. - person Pacane; 19.11.2010