C ++ templates and inheritance

There are buttons in my C ++ environment. The button has a control. Thus, a function that accepts a control can accept Button as an argument. So far so good.

I also have a List < T>. However, the List < Button> is not removed from the < Control> list, which means that a function that accepts a list of controls cannot accept a list of buttons as an argument. It is sad.

Perhaps this is a stupid question, but I don’t see how to solve it :( List < Button > should be displayed from the < Control > list, but I don’t see a way to do this “automatically”.

+6
c ++ inheritance templates
source share
5 answers

I hate to tell you, but if you use a list of Control instances instead of pointers to Control, your buttons will still be garbage (the "slice of objects" of Google). If they are lists of pointers, then either make list<button*> in list<control*> , as others suggested, or make a copy for the new list<control*> from list<button*> and pass this value to the function. Or rewrite the function as a template.

So, if you previously had a function called doSomething that took a list of controls as an argument, you should rewrite it as:

 template <class TControl> void doSomething( const std::list<TControl*>& myControls ) { ... whatever the function is currently doing ... } void doSomethingElse() { std::list<Button*> buttons; std::list<Control*> controls; doSomething( buttons ); doSomething( controls ); } 
+6
source share

Stroustrup has an article about this in his FAQ:

Why can't I assign vector<Apple*> vector<Fruit*>

You can solve it in two ways:

  • Make a list with pointers to Control . Then accept List<Control*>
  • Make your function a template. You can still use List<Button> and List<Control> , but this is more of a template code, not the main one.

Here is the code for the second alternative. The first option is already explained by other answers:

 class MyWindow { template<typename T> void doSomething(List<T> & l) { // do something with the list... if(boost::is_same<Control, T>::value) { // special casing Control } else if(boost::is_same<Button, T>::value) { // special casing Button } } }; 

To limit doSomething only to List<derived from Control> , another code is needed (look for enable_if if you want to know).

Please note that this kind of code (depending on what type you have) should rather be avoided. You have to handle such things with virtual functions. Add the doSomething function to Control and override it in Button.

+7
source share

How about using pointers? Just enter the list<Control*> and put any objects created as a result of the control into it.

+6
source share

Instead of using List, use List, which point to buttons. Thus, your function should accept only one type: List.

+2
source share

Typically, a C ++ way of writing algorithms that execute in a list, sequence, ... is to provide iterators as arguments.

 template < class iterator > doSomething(iterator beg, iterator end); 

This solves the list <Button *> is not removed from the <Control *> list. (using the List <T *> template too, but this kind of creating your common function, but not really)

In my experience, creating good template functions that work on iterators can be very large (too many ...), but this is a "C ++ way" ...

If you are following this route, consider using Boost.ConceptCheck . It will make your life a lot easier.

0
source share

All Articles