Почему я могу вызвать метод базового шаблона класса из производного класса

Я решил протестировать один из примеров в «Эффективном 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, Oни
не знаю, от какого класса он наследует. Конечно это A<T>, но T это параметр шаблона,
тот, который не будет известен до позже (когда B создается) Не зная что T
есть, нет никакого способа узнать, что класс A<T> похоже. В частности, нет способа узнать, есть ли у него f функция.

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

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

7

Решение

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, которое не реализует двухфазный поиск. Он задерживает весь поиск внутри шаблона до второй фазы, и в этот момент поиск завершается успешно.

8

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector