Multiple Types for templated vector

Why do all vector types have to be homogeneous even when using templates. I have tried having multiple types from the same base class in a single vector but I get errors. The only technique that seems to work is the following method
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

 	vector<Object*> items;
	Knife knife;
	Gun gun;
	BaseballBat bat;


	items.push_back(&knife);
	items.push_back(&gun);
	items.push_back(&bat);

	for(int i = 0; i < items.size(); i++)
		items[i]->displayContents();



Is there a way to do the following with templates? When I attempt to implement with templates I get a error informing me that the base class that I am trying to use only has a virtual function without a definition.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    int main()
{
	Items<Object> items;
	Knife knife;
	Gun gun;
	BaseballBat bat;

	items.addItem(knife);
	items.addItem(gun);
	items.addItem(bat);

	items.Size();

	system("Pause");
	return 0;
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template<class T>
class Items
{
private:
	vector<T> collections;

public:
	Items();
	void addItem(T & item);
	void Size();
	void displayItems();

};




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <vector>
#include <boost/variant.hpp>

struct knife { /* ... */ };
struct gun { /* ... */ };
struct bat { /* ... */ };

struct do_something_with
{
    void operator() ( knife& ) const { std::cout << "knife::stab\n" ;}
    void operator() ( gun& ) const { std::cout << "gun::shoot\n" ;}
    void operator() ( bat& ) const { std::cout << "bat::bludgeon\n" ;}
};

int main()
{
    // http://www.boost.org/doc/libs/1_60_0/doc/html/variant/tutorial.html#variant.tutorial.basic
    std::vector< boost::variant< knife, gun, bat > > items { knife{}, gun{}, bat{}, gun{} } ;
    
    for( auto& this_weapon : items ) boost::apply_visitor( do_something_with{}, this_weapon ) ;
}

http://coliru.stacked-crooked.com/a/2edc7203bf9c62a9
Polymorphism only works with pointers and references (and you can't have a vector of references).
The error you're getting is because Object is an abstract class, as it probably should be. But even if you made Object non-abstract, you still wouldn't be able to put heterogeneous objects in a vector.

So, in other words, yes. The only way to have a heterogeneous vector is to make it an std::vector<PolymorphicBase *> and store pointers to derived classes.
Either that, or use a variant type, such as boost::any, or QVariant from Qt. Personally, I prefer to avoid variant types if I can.

EDIT: Yes, exactly like that.
Last edited on
So the first relevant bit of information: you can't have a vector of references. References aren't assignable.

Pointers are the easiest way to handle dynamic containers of polymorphic objects, in your second example you could change to a T* in the vector type and just reference the address in addItem (this presents some major potential headaches with memory management, but it's valid)

The second way to handle this would be allow your objects to be assignable and allow the Items class to make a duplicate that it will store in your collections variable. Which is very likely what you're attempting to do and precisely why it's complaining at you. If you simply pass an Object reference (which can be polymorphic) and it attempts to copy that object into the vector, it has no guarantee that you're not going to pass a generic Object, which is impossible because you haven't implemented some of the virtual methods. (also keep in mind with the second example your vector is exactly this: vector<Object> collections, not vector<Object&> collections, because that's impossible as I mentioned)

The compiler is intelligent, and the language is flexible, but sometimes the finer points are a bit of a balancing act.
Last edited on
Thanks for the responses guys I appreciate it.

One little thing that has been bothering me is, why is this possible
1
2
3
4
5
6
7
	items.push_back(&bat);
	items.push_back(&bat);
	items.push_back(&gun);
	items.push_back(&gun);
	items.push_back(&gun);
	items.push_back(&gun);


In my head, it is almost as if you are declaring the same object multiple times. Since, the vector (with vector<Object*>), is pointing at the same object. Why is it making copies still? Isn't the point of using a vector of pointers to eliminate copies being produced?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <vector>
#include <functional>

struct weapon { virtual ~weapon() = default ; virtual void attack_with() = 0 ; };
struct knife : weapon { virtual void attack_with() override { std::cout << "knife::stab\n" ; } /* ... */ };
struct gun : weapon { virtual void attack_with() override { std::cout << "gun::shoot\n" ; } /* ... */ };
struct bat : weapon { virtual void attack_with() override { std::cout << "bat::bludgeon\n" ; } /* ... */ };

int main()
{
    knife switch_blade ;
    gun beretta ;
    bat cudgel ;

    // http://en.cppreference.com/w/cpp/utility/functional/reference_wrapper
    // 'frequently used as a mechanism to store references inside standard containers'
    std::vector< std::reference_wrapper<weapon> > items { switch_blade, beretta, cudgel } ;

    for( auto& this_weapon : items ) this_weapon.get().attack_with() ;
}

http://coliru.stacked-crooked.com/a/05beb19fd128182f
Topic archived. No new replies allowed.