在C ++模板中进行名称查找

时间:2012-05-17 15:48:55

标签: c++ templates inheritance

我有一些不使用-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 ++标准中有一些要求这种行为的要求。任何人都可以为它提供理由吗?

3 个答案:

答案 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是指基类中的名字 - 毕竟,这个名字甚至不需要存在(想想专业化)!因此,必须通过说nextthis->或者预先List<T>::next向编译器断言using List<T>::next;实际上是一个类成员到您的派生类模板。