2013-09-24 4 views
13

Я пытаюсь перегружать некоторые функции шаблона для выполнения определенных действий, если я называю его с помощью данного класса MyClass или любого производного класса MyClassDer. Вот код:перегрузки функции и наследование

#include <iostream> 

struct MyClass { 
    virtual void debug() const { 
     std::cerr << "MyClass" << std::endl; 
    }; 
}; 

struct MyClassDer : public MyClass { 
    virtual void debug() const { 
     std::cerr << "MyClassDer" << std::endl; 
    }; 
}; 

template <typename T> void func (const T& t) { 
    std::cerr << "func template" << std::endl; 
} 

void func (const MyClass& myClass) { 
    std::cerr << "func overloaded" << std::endl; 
    myClass.debug(); 
} 


int main(int argc, char **argv) { 
    func (1); 
    MyClass myClass; 
    func (myClass); 
    MyClassDer myClassDer; 
    func (myClassDer); 
} 

Выход:

func template 
func overloaded 
MyClass 
func template 

func (myClassDer) вызывает функцию шаблона вместо void func (const MyClass& myClass). Что я могу сделать, чтобы получить ожидаемое поведение?

Благодаря

ответ

3

Вы можете использовать SFINAE:

#include <type_traits> 

template <typename T> 
void func (const T& t, typename std::enable_if<!std::is_base_of<MyClass, T>::value>::type * = nullptr) { 
    std::cout << "func template" << std::endl; 
} 

template < 
    typename T 
    , typename = typename std::enable_if<std::is_base_of<MyClass, T>::value>::type 
> 
void func (const T& t) { 
    std::cout << "func overloaded" << std::endl; 
    t.debug(); 
} 

Если у вас нет C++ 11, подталкивание обеспечивает ту же функциональность.

Live example

EDIT

Это должно работать без C++ 11 (с использованием форсированные):

#include "boost/type_traits.hpp" 

template <typename T> 
void func (const T& t, typename boost::enable_if<!boost::is_base_of<MyClass, T>::value>::type * = 0) { 
    std::cout << "func template" << std::endl; 
} 

template <typename T> 
void func (const T& t, typename boost::enable_if<boost::is_base_of<MyClass, T>::value>::type * = 0) { 
    std::cout << "func overloaded" << std::endl; 
    t.debug(); 
} 
+0

Это, кажется, правильное решение. Я собираюсь посмотреть, как реализовать это с помощью boost. – user2811040

+0

@ user2811040 'boost :: enable_if' и' boost :: is_base_of'. Вот и все. – Angew

+0

@ user2811040 И без C++ 11 переместите второй параметр шаблона в трюк указателя. – Angew

0
MyClass *myClassDer = new MyClassDer; 
func(*myClassDer); 
delete myClassDer; 
+1

Любая причина для введения динамического выделения? – Angew

+0

Перегрузка функций разрешается во время компиляции, поэтому вам нужно, чтобы MyClass'а мог попасть в нужную функцию. Но для работы полиморфизма во время работы вам действительно нужен объект MyClassDer. Следовательно, «новый». – HAL

+0

с использованием динамического распределения ничего не меняет. Полиморфизм отлично работает со ссылками ... – user2811040

0

Вам нужно будет использовать полиморфизм, чтобы вызвать вашу функцию шаблона. Вам нужна ссылка на базовый класс:

int main(int argc, char **argv) { 
    func (1); 
    MyClass myClass; 
    func (myClass); 
    MyClassDer myClassDer; 
    MyClass* mc = &myClassDer; 
    func (*mc); 
} 

More polymorphism examples and details here

+1

Вы могли бы просто создать ссылку и удалить пару '*' –

4

Для почему ваш код не работает: см @ отличное объяснение Дэвида. Для того, чтобы заставить его работать, вы можете использовать SFINAE ("Substition Неудача не Errro), добавив скрытый параметр шаблона Requires (имя только для целей документирования)

template < 
    typename T, typename Requires = typename 
    std::enable_if<!std::is_base_of<MyClass, T>::value, void>::type 
> 
void func (const T& t) { 
    std::cerr << "func template" << std::endl; 
} 

Это отключит этот шаблон для разрешения перегрузки всякий раз, когда T равен или получен из MyClass, и вместо этого выберет правильную функцию (для которой будут выполняться преобразования Derived-to-Base, в отличие от вывода аргументов шаблона, который учитывает только точные соответствия). это и добавьте несколько перегрузок с неперекрывающимися условиями внутри std::enable_if, чтобы иметь мелкозернистый выбор перегрузок функций, которые будут рассмотрены. Но будьте осторожны, SFINAE является тонким!

Live Example.

Note: Я написал свой SFINAE с синтаксисом C++ 11, используя параметр шаблона по умолчанию для шаблонов функций. В C++ 98 вам нужно добавить обычный параметр по умолчанию или изменить тип возвращаемого значения.

+0

Отлично !!!!!! – shofee

0

Его, потому что подпись вашей перегруженной функции является,

void func (const MyClass& myClass) 
{ 
    std::cerr << "func overloaded" << std::endl; 
    myClass.debug(); 
} 

т.е. он хочет MyClass в качестве параметра, и вы вызываете его с помощью MyClassDer. Поэтому во время компиляции он решает другую перегруженную функцию и связывается с ней. Поскольку другая функция шаблонизирована, для компилятора нет никакой проблемы с этим.

Так что если вы хотите передать объект MyClassDer, вы все равно можете это сделать, используя полиморфизм.

MyClass *myClassDer = new MyClassDer; 
func(*myClassDer); 
+4

Не нужно ничего «нового», вы можете создать указатель на локальную переменную (или лучше даже ссылку) –

1

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

Так, во время компиляции лучших перегрузки принять MyClassDer является

func<MyClassDer> (const MyClassDer& t) 

вместо

func<MyClass> (const MyClass& t) 

тогда компилятор выбирает первый.


Возможность решить вопрос является:

func(static_cast<MyClass&>(myClassDer)); 
11

Это просто, как работает разрешение перегрузки. Когда поиск завершается, он находит как шаблон, так и функцию. Затем выбираются типы шаблонов и начинается разрешение перегрузки. В случае аргумента типа MyClass два candiates являются:

void func<MyClass>(MyClass const&); 
void func(MyClass const&); 

Какие одинаково хорошие матчи для аргументов, но второй будучи не-шаблон является предпочтительным. В случае MyClassDer:

void func<MyClassDer>(MyClassDer const&); 
void func(MyClass const&); 

В этом случае первый является лучшим кандидатом, чем второй, а второй требует преобразования производного-к-основания и подобран.

Существуют различные подходы к прямой отправке, чтобы попасть в ваш код. Простейшие просто принуждать тип аргумента, чтобы быть MyClass и, таким образом, возврат к первоначальному делу:

func(static_cast<MyClass&>(myClassDer)); 

Хотя простой, это должно быть сделано везде, и если вы забыли только в одном месте, не то будет называться. Остальные решения - это complex, и вам может потребоваться рассмотреть вопрос о том, не лучше ли было бы предоставлять разные имена функций.

Одним из вариантов является использование SFINAE отключить шаблон, если тип является производным от MyClass:

template <typename T> 
typename std::enable_if<!std::is_base_of<MyClass,MyClassDer>::value>::type 
func(T const & t) { ... } 

В этом случае, после поиска, компилятор выполнит тип вычет, и он будет выводить T к будет MyClassDer, тогда он будет оценивать возвращаемый тип функции (SFINAE также может быть применен к другому шаблону или аргументу функции). is_base_of даст false, а enable_if не будет иметь вложенный тип. Объявление функции будет плохо сформировано, и компилятор потеряет его, оставив разрешение с единственным кандидатом, перегрузкой без шаблонов.

Другим вариантом будет предоставление единого интерфейса шаблона и отправка внутри шаблона или перегрузки (другим именем) с использованием отправки тегов. Идея аналогична, вы оцениваете черту внутри шаблона и вызываете функцию с типом, сгенерированным из этой оценки.

template <typename T> 
void func_impl(T const&, std::false_type) {...} 
void func_impl(MyClass const&, std::true_type) {...} 

template <typename T> 
void func(T const &x) { 
    func_impl(x,std::is_base_of<MyClass,MyClassDer>::type()); 
} 

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

Снова рассмотрим, стоит ли проблема сложности решения. Если вызов func сам выполняется внутри общего кода, простое изменение имени функции решит проблему без излишней сложности, которую вы или другие поддерживающие могут иметь проблемы с сохранением.

+2

Вы набрали этот огромный ответ всего за несколько секунд? +1 – deepmax

+0

@MM .: Больше похоже на пару минут ... Я не медленный типер, но я не Flash –

+0

@MM. Святая Троица поиска имени, вычитание аргументов и перегрузки появляется так часто, что стоит иметь более или менее рабочую память при написании кода шаблона. См. Лекции Стефана Т. Лававей о [Core C++] (http://channel9.msdn.com/Series/C9-Lectures-Stephan-T-Lavavej-Core-C-) – TemplateRex

0

Просто бросить его в базовый тип:

MyClassDer myClassDer; 
func(static_cast<MyClass&>(myClassDer)); 
+0

static_cast не подходит для меня, так как я не могу ожидать, что пользователи моей функции будут использовать эту конструкцию. Более того, моя функция будет фактически оператором! – user2811040

+0

@ user2811040 Ah. И я полагаю, вы хотите, чтобы пользователи вашей функции могли также расширять MyClass.В этом случае вы, кажется, приняли правильный ответ. –

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