Зависимая область действия и вложенные шаблоны - PullRequest
13 голосов
/ 04 июля 2011

Когда я компилирую это:

#ifndef BTREE_H
#define BTREE_H
#include <QList>

template <class T, int degree>
class btree
{
public:
    class node
    {
    public :
        node();
    private:
        node* parent;
        QList<T> values;
        QList<node*> children;
    };
public:
    btree();
    void insert(const T& value);
    node* findLeaf(const T& value);
    void performInsertion(const T& value, node& place);
    //
    node* root;
};
#endif // BTREE_H

Реализация findLeaf такова:

template <class T, int degree>
btree<T,degree>::node* btree<T,degree>::findLeaf(const T &value)
{
    if(root == NULL)
        return root;
}

Эта ошибка возникает:

 error: need ‘typename’ before ‘btree<T, degree>::Node’
 because ‘btree<T, degree>’ is a dependent scope

Ответы [ 2 ]

38 голосов
/ 04 июля 2011

Нет, это не связано с грамматикой C ++, но с ленивым созданием шаблонов C ++ и двухфазный поиск.


В C ++ зависимое имя - это имя или символ, значение которых зависит от одного или нескольких параметры шаблона:

template <typename T>
struct Foo {
    Foo () {
        const int x = 42;
        T::Frob (x);
    }
};

Анализируя этот фрагмент в одиночку, не зная всех будущих значений T, ни один компилятор C ++ не сможет определить, является ли frob в T именем функции, именем типа, чем-то еще или существует ли он вообще.

Чтобы привести пример того, почему это важно, представьте себе некоторые типы, которые вы замените на T:

struct Vietnam {
    typedef bool Frob; // Frob is the name of a type alias
};    

struct Football {
    void Frob (int) {} // Frob is a function name
};

struct BigVoid {};     // no Frob at all!

Поместите их в наш Foo-шаблон:

int main () {
    Foo<Vietnam> fv;   // Foo::Foo would declare a type
    Foo<Football> ff;  // Foo::Foo would make a function call
    Foo<BigVoid> fbv;  // Foo::Foo is not defined at all
}

В этом отношении важна концепция двухфазного поиска . На первом этапе независимый код анализируется и компилируется:

template <typename T>
struct Foo {
    Foo () {
        const int x = 42; // does not depend on T
        T::Frob (x);      // full check skipped for second phase, only rudimentary checking
    }
};

Эта первая фаза позволяет компиляторам выдавать сообщения об ошибках в самом определении шаблона.

Второй этап вызовет ошибки вашего шаблона в сочетании с известным тогда типом T.

Некоторые ранние компиляторы C ++ анализируют шаблоны только после того, как вы их создадите; с этими компиляторами устранение неоднозначности не требовалось, потому что в момент создания экземпляра аргументы шаблона известны. Проблема с этим однофазным поиском состоит в том, что многие ошибки в самом шаблоне не будут обнаруживаться вообще или только в конце компиляции, потому что шаблоны по умолчанию создаются лениво, т.е. только части класса -Tamplate - это расширенные, которые фактически используются, плюс это дает вам больше загадочных сообщений об ошибках, которые, возможно, имеют корень в аргументе шаблона.

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

template <typename T>
struct Foo {
    Foo () {
        const int x = 42;
        typename T::Frob (x);
    }
};

Компилятор теперь знает, что x - это переменная типа Frob:)

22 голосов
/ 04 июля 2011

Грамматика C ++ ужасна, и поэтому при наличии класса шаблона невозможно определить, является ли ::node, на который вы ссылаетесь, переменной / константой или типом.

Стандартпоэтому вы обязуетесь использовать typename перед типами для удаления этой неоднозначности и обрабатывать все другие случаи использования, как если бы это была переменная.

Таким образом *

template <typename T, int degree>
typename btree<T,degree>::node* btree<T,degree>::findLead(T const& value)
^~~~~~~~

является правильной сигнатурой для определения.

...