Reputation: 5201
In order to work with a simplified example, let's consider some animals eating some items in a food list. The food list has a lot of different iterators for different situations.
class contains_fish
{
public:
bool operator () (const Food& food) const;
};
class is_vegetarian
{
public:
bool operator () (const Food& food) const;
};
class FoodList
{
private:
std::vector<Food> foodItems;
public:
typedef std::vector<Food>::iterator iterator;
typedef std::vector<Food>::const_iterator const_iterator;
typedef std::vector<Food>::reverse_iterator reverse_iterator;
typedef std::vector<Food>::const_reverse_iterator const_reverse_iterator;
typedef boost::filter_iterator<contains_fish,FoodList::iterator> fish_iterator;
typedef boost::filter_iterator<contains_fish,FoodList::const_iterator> fish_const_iterator;
typedef boost::filter_iterator<contains_fish,FoodList::reverse_iterator> fish_reverse_iterator;
typedef boost::filter_iterator<contains_fish,FoodList::const_reverse_iterator> fish_const_reverse_iterator;
typedef boost::filter_iterator<is_vegetarian,FoodList::iterator> vegetarian_iterator;
typedef boost::filter_iterator<is_vegetarian,FoodList::const_iterator> vegetarian_const_iterator;
typedef boost::filter_iterator<is_vegetarian,FoodList::reverse_iterator> vegetarian_reverse_iterator;
typedef boost::filter_iterator<is_vegetarian,FoodList::const_reverse_iterator> vegetarian_const_reverse_iterator;
//...
//... with corresponding begin/end functions :
FoodList::iterator begin() { return this->foodItems.begin(); }
FoodList::const_iterator begin() const { return this->foodItems.begin(); }
//...
FoodList::vegetarian_const_reverse_iterator begin_vegetarian_const_reverse() const { return boost::make_filter_iterator<is_vegetarian>(this->foodItems.rbegin(), this->foodItems.rend()); }
};
Now I want to give food (a virtual function) to each animal with iterators on the food list. Something like this code (not working due to virtual template function) :
class Animal
{
public:
virtual ~Animal() {}
template <typename FoodListIterator>
virtual void eat(FoodListIterator begin, FoodListIterator end) = 0;
};
class Dog : public Animal
{
public:
virtual ~Dog() {}
template <typename FoodListIterator>
virtual void eat(FoodListIterator begin, FoodListIterator end)
{
if(begin == end)
std::cout << "Sad day ! Nothing for me..." << std::endl;
else
{
std::cout << "I'm a dog and I'm going to eat :" << std::endl;
for(FoodListIterator it = begin; it != end; ++it)
std::cout << it->toString() << std::endl;
}
}
};
void give_fish(std::vector<Animal*>& animals, const FoodList& food_list)
{
for(unsigned long int i = 0; i < animals.size(); ++i)
animals[i]->eat(food_list.fish_begin(), food_list.fish_end());
}
I have too many different iterator to implement a virtual function for each signature.
How can I do that elegantly, without C++11 ? If it can help, I know the list of types eligible (the list of iterators described in FoodList).
Upvotes: 0
Views: 95
Reputation: 302842
You could reorganize your interface such that each Animal
is fed one single Food
virtually and a range of Food
s non-virtually:
class Animal
{
public:
virtual ~Animal() {}
virtual void eat(Food& ) = 0; // or Food const&
template <typename Iterator>
void eat(Iterator begin, Iterator end) {
for (; begin != end; ++begin) {
eat(*begin);
}
}
};
Though if you really need the whole range, you could use something like boost::any_range
:
class Animal
{
public:
using FoodRange = boost::any_range<Food, boost::forward_traversal_tag,
Food&, std::ptrdiff_t>;
virtual ~Animal() {}
virtual void eat(FoodRange ) = 0;
template <typename Iterator>
void eat(Iterator begin, Iterator end) {
eat(FoodRange{begin, end});
}
};
Upvotes: 1