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

Почему С++ предпочитает этот метод шаблона для перегрузки метода?

Предполагая, что у меня есть два класса, первый для написания примитивных типов (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 на консоль.

Это как мой компилятор должен вести себя? Является ли стандарт С++ явно сказать, что только переопределения, определенные в одном классе (а не базовом классе), должны быть приоритетными по шаблонизированному методу?

4b9b3361

Ответ 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
};