Reputation: 4862
Consider these two functions, which work well for a std::vector:
int connectNode(GraphNode const& newNode,std::vector<GraphNode const*>::const_iterator beginCandidates, std::vector<GraphNode const*>::const_iterator endCandidates){
int connections =0;
for (auto iter= beginCandidates; iter!= endCandidates; ++iter) {
if(connectNodes(newNode,**iter)) ++connections;
}
return connections;
}
int connectNode(GraphNode const& newNode,std::vector<GraphNode>::const_iterator beginCandidates, std::vector<GraphNode>::const_iterator endCandidates){
int connections =0;
for (auto iter= beginCandidates; iter!= endCandidates; ++iter) {
if(connectNodes(newNode,*iter)) ++connections;
}
return connections;
}
These functions work well for vectors but evidently not for any otehr container, e.g. a set. How could they be generalised. The only possible solution I can think of is with some quite ugly enable_if workaround. Is there a straight forward solution ? Edit: To make it clearer: I want both functions, one for normal containers, one for pointer containers. The real logic happens inside connetNodes, which takes two references. (mind the ** in the first function)
Upvotes: 2
Views: 141
Reputation: 109089
Take the iterator as a template parameter, then you don't have to worry what kind of container you're iterating over.
template<class T, class Iter>
int connectNode( GraphNode const& newNode,
Iter beginCandidates,
Iter endCandidates )
{
// ...
}
I don't see the other template parameter (T
) being used anywhere in the function template, but I'm assuming your real code uses it somewhere.
Also, you can static_assert
that the iterator points to a GraphNode const*
using std::iterator_traits<Iter>::value_type
static_assert( std::is_same< typename std::iterator_traits<Iter>::value_type
GraphNode const *>::value,
"Iterator must point to 'GraphNode const *'" );
EDIT:
In order to be able to accept iterators that point to either GraphNode
or GraphNode const *
leave the function template signature the same, but create 2 overloads for a helper that calls connectNodes
bool do_connectNodes( GraphNode const& newNode, GraphNode const *p )
{
return connectNodes( newNode, *p );
}
bool do_connectNodes( GraphNode const& newNode, GraphNode& n )
{
return connectNodes( newNode, n );
}
Now, within connectNode
change the if
condition to
if( do_connectNodes(newNode,*iter) ) ++connections;
and the correct overload will be selected depending on what the iterator is pointing to.
Upvotes: 2
Reputation: 131789
As was said, make the iterator type a template parameter - this solves the issue of generalizing the iterator itself. For differing between normal GraphNode
values, and pointers thereof, you can just use overloading:
template<class T>
T& maybe_deref(T& v){ return v; }
template<class T>
T& maybe_deref(T* p){ return *p; }
And just call that in connectNodes(newNode, maybe_deref(*iter))
.
Upvotes: 2