c++ templating woes

Started by
8 comments, last by Julian90 16 years, 10 months ago
First let me explain some background to the problem so it's easier to understand why I'm doing this crazy thing. I'm making game and am currently working on collision. In order to reduce the number of collision tests (by about 80%), I divided up my objects into different containers based on what types of objects they can collide into. Now in order to keep my head from exploding, I'm trying to keep a uniform interface for each container, even though it stores different kinds of data. So my data containers are std::vectors (I know, I know... but I didn't have to write anything new!) Here's the problem: I want to write a function that takes an iterator to any of these vectors, and a reference to any of the vectors, and that function removes the correct object from the correct vector. So all the objects in my containers are all derived from CObjectBase. I was hoping all my containers would automatically upconvert to std::vector<CObjectBase*> for the purpose of this function, but they don't. In case I'm not being clear at all, here's the function:

E_VAL RemoveByIterator(std::vector<CObjectBase*> vect, std::vector<CObjectBase*>::iterator iter);

E_VAL CObjectList::RemoveByIterator(std::vector<CObjectBase*> vect, std::vector<CObjectBase*>::iterator iter)
{
	SafeDelete(*iter);
	vect.erase(iter);
	m_numVertices -= 6;
	m_numPolygons -= 2;

	return RET_OK;
}
I'm getting compiler errors that it cannot convert the parameters because my containers are not of type <CObjectBase*>, but they are of classes that are derived from CObjectBase*. How can I write this function?
Advertisement
Would this work?
template < class A, class B >E_VAL RemoveByIterator(std::vector<A*> vect, std::vector<B*>::iterator iter);


Note that I'm not sure on side-effects this might have, or other problems you might run into, since I don't know the rest of the dependencies or relation between vect and iter, or how you call them (is the type known at call site).
To remove an element from std::vector<T>, it must be done by an iterator of that container (std::vector<T>::iterator), so you cant do a template function that uses a container with a different iterator, but you can do this (similar, but not the same thing):

template <class T>E_VAL CObjectList::RemoveByIterator(std::vector<T> vect, typename std::vector<T>::iterator iter){	SafeDelete(*iter);	vect.erase(iter);	m_numVertices -= 6;	m_numPolygons -= 2;	return RET_OK;}


Edit: Fixed typename.
How the heck did you answer my question so fast! :)

The type *IS* always known from the call site, if that helps. Also both types are always the same, as in, class B == class A. However, I am getting a problem with your solution. Compiler says
Warning ::iterator "dependedent name is not a type"

Error Syntax error "identifier : iterator"

on the function definition. (Which is just like what you wrote, and removing the B does not change anything) And it repeats that error 3 times, btw.

edit: that was in response to antheus
edit 2: also tested jaiminho's fix, (with and without *'s on the T's), same error.
template < class T >E_VAL RemoveByIterator(std::vector<T*> vect, typename std::vector<T*>::iterator iter);
Ya, forgot about the typename. Edited mine, above.
OK, I get the idea of what you guys are doing to solve my problem, but for some reason it's not working with the iterator.

This compiles until I uncomment:
	template<class A>		E_VAL RemoveByIterator(std::vector<A> vect/*, std::vector<A>::iterator iter*/);/*********/template<class A>	E_VAL CObjectList::RemoveByIterator(std::vector<A> vect/*, std::vector<A>::iterator iter*/){	//SafeDelete(*iter);	//vect.erase(iter);	m_numVertices -= 6;	m_numPolygons -= 2;	return RET_OK;}


I get the error I posted above when I uncomment the iterator. I'll repost here for convenience:
warning C4346: 'std::vector<A>::iterator' : dependent name is not a type
error C2061: syntax error : identifier 'iterator'

I get that warning+error 3 times on the header, and 1 time on the function itself.

edit: whether I have std::vector<A> or std::vector<A*> has no impact on compiler errors whatsoever.
edit again: unrelated question, how do you get that cool code box, antheus?
edit again again: just noticed that word "typename" !!!!!!!!
As Antheus said in a previous post you have to add the typename in front of the iterator parameter to the function.
Thanks, apparently I just can't read! Now it works! :)

edit:

Well, it compiles, but it doesn't work.... the erase line causes a crash every time.... says erase iterator is outside range

edit2: final fix:

Fiddled around with it, the first operand needed to be a reference and now it works, strangely there is no discernable difference between using A* compared to using A... anyone know why?

[Edited by - polaris2013 on June 10, 2007 7:58:27 PM]
Quote:Fiddled around with it, the first operand needed to be a reference and now it works, strangely there is no discernible difference between using A* compared to using A... anyone know why?


There is a difference, you just wont notice it if your working with vectors of pointers. When you use A the compiler will deduce that A = SomeType*, however when you use A* it will deduce A = SomeType (for a std::vector<SomeType*>) and so if you try to use the A* version with a std::vector<int> it will fail to compile because there is no type A for which A* = int.

From your description it sounds like the failure would be a desired behavious to prevent mistakes, and even better would be if it fails for types not derived from CObjectBase in which case I would use something like this:

#include <boost/type_traits/is_base_of.hpp>#include <boost/enable_if.hpp>template<typename T>boost::enable_if<boost::is_base_of<CObjectBase, T>, E_VAL>::typeCObjectList::RemoveByIterator(std::vector<T*> vect, typename std::vector<T*>::iterator iter);


Also a note: If the order of the elements in the vector doesn't matter then you should be "swap and poping" (i.e. std::swap the element you want to remove with the last element then std::vector<T>::pop_back())

<rant>
Drop the C on the class names, you know its a class and the compiler will tell you if you try to use it incorrectly. Having the C is duplicated information which will have to be updated if you ever decide you want to make it something other then a class leading to additional maintenance costs.
</rant>

This topic is closed to new replies.

Advertisement