2013-10-02 3 views
5

Я решил проверить один из примеров в «Эффективном C++», и я не получаю ожидаемого результата. Таким образом, по-видимому, это (упрощенный) код не должен составлять:Почему я могу вызвать метод класса базового шаблона из производного класса

template <class T> 
struct A { 
    void f(){} 
}; 

template <class T> 
struct B : public A <T> { 
    void f2() { f(); } // calling base function - will not compile 
}; 

Вот объяснение (имена классов изменены для простоты):

Кода выше не будет компилироваться, по крайней мере, с совместимым компиляторы. Такие компиляторы будут жаловаться, что f не существует. Мы видим, что f находится в базовом классе, но компиляторы там его не ищут.

Нам нужно понять, почему. Проблема заключается в том, что когда компиляторы сталкиваются с определением для шаблона класса B, они не знают, на каком классе он наследует. Конечно, это A<T>, но T - это параметр шаблона, тот, который не будет известен до позже (когда создается экземпляр B). Не зная, что T есть, нет способа узнать, что выглядит класс A<T>. В частности, нет способа узнать, имеет ли он функцию f.

Мой компилятор (Visual Studio) не против ... Он даже не показывает никаких предупреждений.

Является ли приведенный выше код правильным или нет?

+0

f() должно быть полностью квалифицировано с именем базового класса или 'this->' – goji

+0

@Troy "f() должно быть полностью квалифицированным .." извините, что это значит? – Oleksiy

+1

@Oleksiy Он работает на MSVC, потому что этот компилятор не реализует двухфазный поиск имени и, следовательно, не пытается разрешить зависимые имена до создания шаблона. Во время создания экземпляра 'A :: f' больше не является зависимым именем, так как слово' T' известно. И * полностью квалифицированный * означает, что он будет работать, если вы напишете 'A :: f()'. C++ - faq объясняет ловушки использования этого подхода. – Praetorian

ответ

5
template <class T> 
struct A { 
    void f(){} 
}; 

template <class T> 
struct B : public A <T> { 
    void f2() { f(); } // calling base function - will not compile 
}; 

В производном шаблоне, выражение f() не зависит от любого аргумента шаблона, так что компилятор пытается решить ее в течение первой фазы поиска. На этом этапе шаблон еще не был создан с типом, и компилятор не будет смотреть в базу A<T>. Причина в том, что компилятор не мог знать, существует ли для типа экземпляра специализация A<T>, которая может не содержать каких-либо членов.

Решение состоит в том, чтобы сделать выражение зависит, самым простым способом было бы квалифицировать с this->:

template <typename T> 
void B<T>::f2() { this->f(); } 

Как выражение теперь зависит, поиск задерживается до второй фазы, где замещенный тип и A<T> - это конкретный тип. Другая альтернатива является квалификация с классом, где она определена:

template <typename T> 
void B<T>::f2() { A<T>::f(); } 

Опять же выражение становится зависимым и будет решено в ходе второго этапа. Основное различие заключается в том, что в этом втором случае вызов квалифицирован и, следовательно, не использует динамическую отправку. Если A<T>::f() был виртуальным, он все равно выполнил бы A<T>::f(), а не окончательный повод.


Правильно ли это код? Нет. VS принимает его? Да.

Это известное несоответствие в компиляторе Visual Studio, которое не реализует двухэтапный поиск. Это задерживает весь поиск внутри шаблона во второй фазе и в этом случае поиск выполняется успешно.

+0

Другой способ - использовать' :: f; ' , или моя любимая идиома, используя B :: A :: f; '. – Potatoswatter

Смежные вопросы