我的问题是,为什么以下代码无法编译:
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) {}
};
虽然如下:
class c1
{
public:
typedef int type_name;
void fn1(type_name x) {}
};
class c2 : public c1
{
public:
void fn2(type_name x) {}
};
如您所见,唯一的区别是在第一种情况下,类是模板。 Gcc和Clang抱怨在第二个类中没有定义type_name(仅在模板版本中)。 typedef是不是从父类继承的?如果是这样,为什么它适用于非模板版本?从模板中使用typedef时是否有异常?
另外,我知道我可以使用完全限定的类型名称,即'typename c1 :: type_name'。我只是想知道这是否是一些C ++限制或编译器错误。
答案 0 :(得分:3)
他们 实际上&#34;继承&#34; (我的意思是他们可以作为c2<t>
的成员访问)。但是,当编译器有理由相信它们依赖时,所有从依赖基类(依赖于模板参数的基类)继承的成员(不仅仅是类型)都只在类模板中查找。
换句话说,在解析模板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) {}
};
在这两种情况下,::
左侧的内容取决于模板参数,因此编译器会知道推迟查找直到实例化。
另请注意,您需要添加typename
关键字;没有它,编译器就不知道::
右边的东西是类型还是非类型成员。在这种情况下,语言标准要求将其视为非类型成员,这会导致语法错误。
但是,当c2
不是模板时,所有基类的定义在解析时都是完全已知的,因此查找名称没有问题。
您可以在此somewhat related SO question中了解有关此两阶段查找的更多详细信息。