43

我有一些 C++ 代码在没有 -fpermissive 选项的情况下不再编译。这是我不能分享的专有代码,但我认为我已经能够提取一个简单的测试用例来证明这个问题。这是 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);
}

我不是想找出如何修复代码以使其再次编译。这只是将 next(item) 更改为 this->next(item) 的问题,我试图更好地理解为什么这种更改是必要的。我在此页面上找到了解释:http: //gcc.gnu.org/onlinedocs/gcc/Name-lookup.html 虽然这个解释很有用,但我仍然有一些问题。我的函数采用 T*(指向类型 T 的指针)这一事实不应该使它依赖于模板参数。用我自己的话说,编译器(gcc 4.7)不应该能够找出 next() 函数在基类 List 中吗?为什么有必要在每个这样的调用前面加上 this->?我注意到 clang 3.1 表现出相同的行为,所以我假设 c++ 标准中有一些要求需要这种行为。任何人都可以为此提供理由吗?

4

3 回答 3

63

问题是模板分两次处理(根据标准,VS 不这样做)。在第一遍中,在类型替换之前,查找并检查不依赖于模板参数的所有内容。一旦类型被替换,从属名称将在第二遍中解析。

现在,在第一遍中,没有任何内容表明它next依赖于模板参数,因此它需要在类型替换之前解析。现在,因为基本类型是在当前模板的模板参数上模板化的,编译器无法查看它(它可能专门用于某些类型,并且在不知道T我们用什么类型实例化模板的情况下,我们不知道要使用哪种专门化使用,即基础取决于T我们在知道之前检查T)。

The trick of adding this-> turns next into a dependent name, and that in turn means that lookup is delayed until the second pass, where T is known, and because T is known, List<T> is also known and can be looked up into.


EDIT: One important detail missing in the wording of the answer above is that second phase lookup (after type substitution) will only add functions found during argument dependent lookup. That is, if next was a free function in a namespace associated with T it would be found, but it is a member on the base, which is not visible for ADL on T.

于 2012-05-17T16:05:21.197 回答
14

你需要写成this->

this->next(item);

这里this->的部分是必需的,因为next()它是从模板库继承的成员,如果您仔细阅读错误消息,建议它本身:

'List<int>'template_eg.cpp:18:25:注意:非限定查找找不到依赖基中的声明template_eg.cpp
:18:25:注意:改用'this->next'

阅读这篇解释了 C++ 中的两阶段名称查找的文章:

于 2012-05-17T15:52:26.200 回答
5

如果你的基类是一个模板实例,那么就没有办法知道它next引用了基类中的一个名称——毕竟,这个名称甚至不需要存在(考虑专业化)!因此,必须通过说、 或或通过在派生类模板前面添加来向编译器断言next实际上是类成员。this->List<T>::nextusing List<T>::next;

于 2012-05-17T15:51:54.130 回答