Подтвердить что ты не робот

Поиск имен в шаблонах С++

У меня есть код С++, который больше не компилируется без опции -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);
}

Я не пытаюсь выяснить, как исправить код, чтобы он снова скомпилировался. Это просто вопрос изменения следующего (элемента) этого- > следующего (элемента) Я пытаюсь лучше понять, почему это изменение необходимо. Я нашел объяснение на этой странице: http://gcc.gnu.org/onlinedocs/gcc/Name-lookup.html Хотя это объяснение было полезным, у меня все еще есть некоторые вопросы. Не должен ли факт, что моя функция принимает T * (указатель на тип T), делает ее зависимой от аргумента шаблона. В моей собственной формулировке, не должен ли компилятор (gcc 4.7) понять, что функция next() находится в списке базового класса? Почему это необходимо перед этим-перед этим вызовом? Я заметил, что clang 3.1 демонстрирует такое же поведение, поэтому я предполагаю, что в стандарте С++ есть какое-то требование, требующее такого поведения. Может ли кто-нибудь объяснить это?

4b9b3361

Ответ 1

Проблема в том, что шаблоны обрабатываются за два прохода (в соответствии со стандартом, VS делает иначе). В первом проходе, перед подстановкой типа, все, что не зависит от аргументов шаблона, проверяется и проверяется. Зависимые имена затем оставляются для разрешения во втором проходе после замены типа.

Теперь в первом проходе нет ничего, что указывало бы, что next зависит от аргументов шаблона и, следовательно, его нужно разрешить перед заменой типа. Теперь, поскольку базовый тип шаблонизирован в аргументе шаблона вашего текущего шаблона, компилятор не может его изучить (он может быть специализированным для некоторых типов и не зная, какой тип T мы создаем с шаблоном, мы не можем знать какую специализацию использовать, т.е. база зависит от T, и мы проверяем, прежде чем знать T).

Трюк добавления this-> превращает next в зависимое имя, а это, в свою очередь, означает, что поиск задерживается до второго прохода, где T известен, и поскольку T известен, List<T> > также известен и может быть просмотрен.


РЕДАКТИРОВАНИЕ. Одна важная деталь, отсутствующая в формулировке ответа выше, заключается в том, что поиск второй фазы (после подстановки типов) добавит только функции, найденные во время поиска зависимого от аргумента. То есть, если next была свободной функцией в пространстве имен, связанном с T, она была бы найдена, но она является членом на основе, которая не видна для ADL на T.

Ответ 2

Вам нужно написать this-> как:

this->next(item);

Здесь this-> требуется часть, потому что next() является наследуемым членом из базы шаблонов, и если вы внимательно прочитали сообщение об ошибке, там также предлагается:

template_eg.cpp: 18: 25: note: объявления в зависимой базе 'List<int>' не найдены неквалифицированным поиском
template_eg.cpp: 18: 25: note: использовать 'this->next' вместо

Прочитайте эту статью, которая объяснила поиск двухфазного имени в С++:

Ответ 3

Если ваш базовый класс является экземпляром шаблона, тогда нет способа узнать, что next ссылается на имя в базовом классе - в конце концов, имя не должно даже существовать (подумайте об специализациях)! Таким образом, вы должны утверждать компилятору, что next на самом деле является членом класса, говоря this-> или List<T>::next, или добавив using List<T>::next; к шаблону производного класса.