依赖范围和嵌套模板

sor*_*h-r 13 c++ templates nested-class

当我编译这个:

#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
Run Code Online (Sandbox Code Playgroud)

findLeaf的实现是这样的:

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

发生此错误:

 error: need ‘typename’ before ‘btree<T, degree>::Node’
 because ‘btree<T, degree>’ is a dependent scope
Run Code Online (Sandbox Code Playgroud)

Seb*_*ach 38

不,这与C++的语法无关,而是与C++模板的懒惰实例化和两阶段查找有关.


在C++中,从属名称是一个名称或符号,其含义取决于一个或多个模板参数:

template <typename T>
struct Foo {
    Foo () {
        const int x = 42;
        T::Frob (x);
    }
};
Run Code Online (Sandbox Code Playgroud)

通过单独解析该片段,而不知道T的所有未来值,没有C++编译器可以推断出frobin 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!
Run Code Online (Sandbox Code Playgroud)

把它们放到我们的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
}
Run Code Online (Sandbox Code Playgroud)

与此相关的是两阶段查找的概念.在第一阶段,解析和编译非依赖代码:

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
    }
};
Run Code Online (Sandbox Code Playgroud)

第一阶段是编译器在模板定义中发出错误消息的原因.

第二阶段将与当时已知的类型T一起触发模板的错误.

一些早期的C++编译器只会在实例化后解析模板; 对于那些编译器,不需要消除歧义,因为在实例化时,模板参数是已知的.这个单阶段查找的问题在于模板本身中的许多错误根本不会被检测到或者仅在编译的后期检测到,因为模板默认是懒惰地实例化,即只扩展了类模板的一部分.实际使用,加上它会给你更多神秘的错误信息,可能是在模板参数的根源.

因此,为了使两阶段查找起作用,您必须帮助编译器.在这种情况下,您必须使用typename以告诉编译器您的意思是一种类型:

template <typename T>
struct Foo {
    Foo () {
        const int x = 42;
        typename T::Frob (x);
    }
};
Run Code Online (Sandbox Code Playgroud)

编译器现在知道x是Frob类型的变量:)


Mat*_* M. 22

C++语法是可怕的,因此当给定模板类时,不可能知道::node你引用的是变量/常量还是类型.

因此,标准要求您typename在类型之前使用以消除此歧义,并将所有其他用法视为变量.

从而

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

是定义的正确签名.