我有一些不使用-fpermissive选项而不再编译的C ++代码。 这是我不能分享的专有代码,但我认为我已经能够提取一个简单的测试用例来证明这个问题。这是g ++的输出
template_eg.cpp: In instantiation of 'void Special_List<T>::do_other_stuff(T*) [with T = int]':
template_eg.cpp:27:35: required from here
template_eg.cpp:18:25: error: 'next' was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
template_eg.cpp:18:25: note: declarations in dependent base 'List<int>' are not found by unqualified lookup
template_eg.cpp:18:25: note: use 'this->next' instead
所以这是产生问题的代码:
template<class T> class List
{
public:
void next(T*){
cout<<"Doing some stuff"<<endl;
}
};
template<class T> class Special_List: public List<T>
{
public:
void do_other_stuff(T* item){
next(item);
}
};
int main(int argc, char *argv[])
{
Special_List<int> b;
int test_int = 3;
b.do_other_stuff(&test_int);
}
我不是要找出如何修复代码以使其再次编译。 这只是将下一个(项目)改为此 - >下一个(项目)的问题 我想更好地理解为什么这种改变是必要的。 我在此页面上找到了解释:http://gcc.gnu.org/onlinedocs/gcc/Name-lookup.html 虽然这个解释很有用,但我还是有一些问题。不应该我的函数采用T *(指向类型T的指针)的事实使它依赖于模板参数。 在我自己的措辞中,编译器(gcc 4.7)是否应该能够找出next()函数在基类List中? 为什么有必要在前面加上这个&gt;在每一次这样的电话面前? 我注意到clang 3.1表现出相同的行为,所以我假设c ++标准中有一些要求这种行为的要求。任何人都可以为它提供理由吗?
答案 0 :(得分:56)
问题是模板经过两次处理(根据标准,否则VS)。在第一遍中,在类型替换之前,查找并检查不依赖于模板参数的所有内容。一旦类型被替换,依赖名称将在第二遍中解析。
现在,在第一遍中,没有任何内容表明next
依赖于模板参数,因此需要在类型替换之前解析 。现在,因为基类型是在当前模板的模板参数上模板化的,所以编译器无法查看它(它可能专门用于某些类型,并且不知道我们用实例化模板的T
类型,我们无法知道要使用哪个专业化,即基础取决于 T
,我们在知道T
之前检查。
添加this->
的诀窍将next
转换为依赖名称,这反过来意味着查找会延迟到第二次传递,其中T
已知,并且因为{{ 1}}已知,T
也是已知的,可以查阅。
编辑:上面答案的措辞中缺少的一个重要细节是第二阶段查找(在类型替换之后)将仅添加在参数依赖查找期间找到的函数。也就是说,如果List<T>
是与next
关联的命名空间中的自由函数,则会找到它,但它是基础上的成员,T
上的ADL不可见。
答案 1 :(得分:9)
您需要将this->
写为:
this->next(item);
此处this->
部分是必需的,因为next()
是模板基础的继承成员,如果您仔细阅读错误消息,则会自行建议:
template_eg.cpp:18:25:注意:不合格的查找找不到从属基础
'List<int>'
中的声明 template_eg.cpp:18:25:注意:改为使用'this->next'
阅读本文,其中介绍了用C ++编写的两阶段名称查找:
答案 2 :(得分:5)
如果你的基类是一个模板实例,那么就无法知道next
是指基类中的名字 - 毕竟,这个名字甚至不需要存在(想想专业化)!因此,你必须通过说next
或this->
或者预先List<T>::next
向编译器断言using List<T>::next;
实际上是一个类成员到您的派生类模板。