Почему С++ предпочитает этот метод шаблона для перегрузки метода?
Предполагая, что у меня есть два класса, первый для написания примитивных типов (bool
, int
, float
и т.д.), а второй - первый, который также записывает сложные типы:
struct Writer {
virtual void Write(int value) = 0;
};
struct ComplexWriter : public Writer {
template <typename TValue> void Write(const TValue &value) {
boost::any any(value);
Write(any);
}
//virtual void Write(int value) = 0; // see question below
virtual void Write(const boost::any &any) = 0;
};
Идея состоит в том, что если кто-то звонит myWriter.Write(someIntValue);
, то перегрузка int получит приоритет над шаблоном.
Вместо этого мой компилятор (Visual С++ 11.0 RC) всегда выбирает метод шаблона. Следующий фрагмент кода, например, напечатает Wrote any
на консоли:
struct ComplexWriterImpl : public ComplexWriter {
virtual void Write(int value) { std::cout << "Wrote an int"; }
virtual void Write(const boost::any &any) { std::cout << "Wrote any"; }
};
void TestWriter(ComplexWriter &writer) {
int x = 0;
writer.Write(x);
}
int main() {
ComplexWriterImpl writer;
TestWriter(writer);
}
Поведение внезапно меняется, когда я объявляю метод Write(int)
в классе ComplexWriter
(см. прокомментированную строку в первом фрагменте). Затем он выводит Wrote an int
на консоль.
Это как мой компилятор должен вести себя? Является ли стандарт С++ явно сказать, что только переопределения, определенные в одном классе (а не базовом классе), должны быть приоритетными по шаблонизированному методу?
Ответы
Ответ 1
Проблема в том, что в момент, когда вы вызываете writer.Write(x)
, компилятор видит ComplexWriter
не a ComplexWriterImpl
, поэтому он знает только о функциях, определенных в ComplexWriter
- функции шаблона и boost::any
.
ComplexWriter
не содержит виртуальных функций, принимающих int
, поэтому он не имеет возможности переходить в перегрузку int, определенную в ComplexWriterImpl
Когда вы добавляете в виртуальную перегрузку класс ComplexWriter
, тогда компилятор осознает, что существует целая перегрузка в классе ComplexWriter
и поэтому вызывает ее реализацию в ComplexWriterImpl
EDIT: теперь, когда вы отредактировали в наследовании между ComplexWriter и Writer, у меня есть более полное объяснение для вас:
Когда вы создаете подкласс и определяете в нем функцию, тогда все функции этого имени в базовом классе будут скрыты независимо от их типов аргументов.
Вы можете обойти это с помощью ключевого слова using, которое, как я полагаю:
struct ComplexWriter : public Writer {
template <typename TValue> void Write(const TValue &value) {
boost::any any(value);
Write(any);
}
using Writer::Write;
virtual void Write(const boost::any &any) = 0;
};
Подробнее см. в этой записи в FAQ: http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.9
EDIT 2: просто чтобы подтвердить, что это действительно решит вашу проблему: http://ideone.com/LRb5a
Ответ 2
Когда вы обращаетесь к объекту через интерфейс ComplexWriter
, компилятор попытается разрешить вызов функции Write(int)
с помощью определений в этом классе. Если он не сможет этого сделать, он рассмотрит базовые классы.
В этом случае у вас есть два кандидата: Write(any)
и шаблонная версия. Поскольку на данный момент нет явного Write(int)
, ему придется выбирать между этими двумя параметрами. Write(any)
требует неявного преобразования, в то время как версия с шаблоном не работает, поэтому вызывается шаблонная версия (которая, в свою очередь, вызывает Write(any)
).
Чтобы сделать Write(int)
из Writer
доступным, импортируйте функции Writer::Write
:
class ComplexWriter : public Writer
{
using Writer::Write;
// rest is as before
};