为什么类级别的 typedef 不从模板继承?

haa*_*ael 3 c++ inheritance templates typedef

我的问题是,为什么以下代码无法编译:

template<typename t> class c1
{
public:
    typedef int type_name;
    void fn1(type_name x) {}
};

template<typename t> class c2 : public c1<t>
{
public:
    void fn2(type_name x) {}
};
Run Code Online (Sandbox Code Playgroud)

虽然以下内容确实如此:

class c1
{
public:
    typedef int type_name;
    void fn1(type_name x) {}
};

class c2 : public c1
{
public:
    void fn2(type_name x) {}
};
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,唯一的区别是在第一种情况下,类是模板。Gcc 和 Clang 抱怨 type_name 没有在第二个类中定义(仅在模板版本中)。typedef不是从父类继承的吗?如果是这样,为什么它可以在非模板版本上工作?使用模板中的 typedef 时是否存在异常?

另外,我知道我可以使用完全限定的类型名称(即“typename c1::type_name”)来完成这项工作。我只是想知道这是否是一些 C++ 限制或编译器错误。

Rei*_*ica 5

它们实际上“继承的”(我的意思是它们可以作为 的成员访问c2<t>)。但是,只有当编译器有理由相信它们是依赖的时,从依赖基类(依赖于模板参数的基类)继承的所有成员(不仅仅是类型)才会在类模板内查找。

换句话说,在解析 template 的定义时c2,编译器不知道t实例化时会是什么,因此它无法猜测 的定义c1<t>会是什么(请记住,它可能稍后会被专门化)。c1因此,在查找 中找到的名称时,它根本不考虑c2。所以type_name没找到。

但是,如果您以某种方式使名称查找显式依赖于模板参数,编译器将意识到它必须推迟查找直到实例化。在这些情况下会发生这种情况:

// Case 1
template<typename t> class c2 : public c1<t>
{
public:
    void fn2(typename c1<t>::type_name x) {}
};

// Case 2
template<typename t> class c2 : public c1<t>
{
public:
    void fn2(typename c2::type_name x) {}
};
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,左侧的内容::取决于模板参数,因此编译器将知道推迟查找直到实例化。

另请注意,您需要添加关键字typename;如果没有它,编译器将不知道右侧的内容::是类型成员还是非类型成员。语言标准规定在这种情况下将其视为非类型成员,这会导致语法错误。

但是,当c2不是模板时,解析时就完全知道其所有基类的定义,因此查找名称没有问题。

您可以在这个有点相关的 SO 问题中了解有关此两阶段查找的更多详细信息。