2012-06-07 1 views
12

Предполагая, что у меня было два класса, первые из них для записи примитивных типов (bool, int, float и т.д.), а второй проход первого и писать сложные типы:Почему C++ предпочитает этот метод шаблона методу перегрузки?

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 C++ 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.

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

+2

Я не вижу, где вы перегрузить метод, принимающий Int с одним шаблонными. У вас есть два несвязанных класса: 'Writer' с методом, принимающим' int' и 'ComplexWriter' с шаблонизированным методом (и методом' boost :: any const & '). В вашем тестовом коде вы используете 'ComplexWriter', поэтому, конечно, он будет называть членом' ComplexWriter'. Если вы действительно ожидали, что он назовет члена совершенно несвязанного класса 'Writer', вы можете объяснить, почему (и как) он должен это делать? – celtschk

+2

Вы упомянули, что 'ComplexWriter' расширяет' Writer', но в коде, который вы указали, он не наследуется от 'Writer'. Это правильная настройка? – Attila

+0

Спасибо, что указали это. Да, ComplexWriter должен был получить от Writer. Я соответствующим образом обновил этот вопрос. Результат все тот же (хотя, конечно, забывая указать, что базовый класс подключается к ядру моего вопроса.). – Cygon

ответ

7

Проблема заключается в том, что в точке вы вызываете writer.Write(x) компилятор видит ComplexWriter не ComplexWriterImpl, поэтому известно только из функций, определенных в ComplexWriter - функции шаблона и функции boost::any.

ComplexWriter не содержит каких-либо виртуальных функций, принимать int, так что нет никакого способа, чтобы позвонить через к перегрузке INT, определенной в ComplexWriterImpl

При добавлении в виртуальной перегрузки в ComplexWriter класса, то компилятор становится известно, что существует целое число от перегрузки в ComplexWriter класса и, следовательно, требует до его реализации в ComplexWriterImpl

EDIT: Теперь, когда вы изменили в наследство между ComplexWriter & Writer, я получил более полное объяснение вы:

Когда вы создаете подкласс и определяете в нем функцию, тогда все функции этого имени в базовом классе будут скрыты независимо от их типов аргументов.

Вы можете обойти эту проблему с помощью ключевого слова Я считаю:

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

+0

Да, я забыл указать базовый класс в своем фрагменте. ComplexWriter наследует от Writer, но поведение, которое я наблюдаю, так я объяснял, даже если базовый класс правильно указан. – Cygon

+0

@ Кигон Да, заметил это. Я просто пытаюсь придумать объяснение/редактирование только сейчас. Хотя я могу в конечном итоге удалить этот ответ, если я потерплю неудачу! – obmarg

+0

Спасибо за быстрый ответ в любом случае! Забыть указать базовый класс было действительно глупой ошибкой сделать, поскольку он исказил весь вопрос :) – Cygon

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 
}; 
Смежные вопросы