Reputation: 10314
I wrote these classes that represent a graph:
class Node{
public:
typedef std::vector<Node>::iterator iterator;
iterator begin() {return neigh.begin();}
iterator end() {return neigh.end();}
public:
Node(std::string n) : name(n) {}
std::string getName() {return name; }
void addNeigh(Node n){
neigh.push_back(n);
}
private:
std::string name;
std::vector<Node> neigh;
};
class Map{
public:
typedef std::vector<Node>::iterator iterator;
iterator begin() {return nodes.begin();}
iterator end() {return nodes.end();}
public:
Map(std::string n) : map_name(n) {}
void addNode(Node n){
nodes.push_back(n);
}
std::string getName() { return map_name; }
int getNumNodes() {return nodes.size();}
private:
std::string map_name;
std::vector<Node> nodes;
};
Then i want to write a function printGraph
that prints some graph informations using a traits:
template<typename T>
struct GraphTraits{
};
template<>
struct GraphTraits<Map>{
typedef Map::iterator node_iterator;
static node_iterator node_begin(Map map){ return map.begin();}
static node_iterator node_end(Map map) { return map.end();}
static std::string getName(Map m) {return m.getName();}
};
template<typename T>
void printGraph(T t){
std::cout << std::endl
<< "--------------------------------------" << std::endl
<< "| printGraph |" << std::endl
<< "--------------------------------------" << std::endl;
std::cout << "Graph name: " << GraphTraits<T>::getName(t) << std::endl;
for(GraphTraits<T>::node_iterator it = GraphTraits<T>::node_begin(t), e = GraphTraits<T>::node_end(t);
it != e; ++it) {
std::cout<< (*it).getName() << std::endl; }
}
If i try to compile this code i get these errors:
grafo.cpp:75:37: error: expected ';' in 'for' statement specifier
for(GraphTraits<T>::node_iterator it = GraphTraits<T>::node_begin(t), e = GraphTraits<T>::node_end(t);
^
grafo.cpp:75:37: error: use of undeclared identifier 'it'
grafo.cpp:75:73: error: use of undeclared identifier 'e'
for(GraphTraits<T>::node_iterator it = GraphTraits<T>::node_begin(t), e = GraphTraits<T>::node_end(t);
^
grafo.cpp:76:9: error: use of undeclared identifier 'it'
it != e; ++it) {
^
grafo.cpp:76:15: error: use of undeclared identifier 'e'
it != e; ++it) {
^
grafo.cpp:76:16: error: expected ')'
it != e; ++it) {
^
grafo.cpp:75:6: note: to match this '('
for(GraphTraits<T>::node_iterator it = GraphTraits<T>::node_begin(t), e = GraphTraits<T>::node_end(t);
^
grafo.cpp:76:20: error: use of undeclared identifier 'it'
it != e; ++it) {
^
grafo.cpp:75:23: error: unexpected type name 'node_iterator': expected expression
for(GraphTraits<T>::node_iterator it = GraphTraits<T>::node_begin(t), e = GraphTraits<T>::node_end(t);
^
grafo.cpp:104:3: note: in instantiation of function template specialization 'printGraph<Map>' requested here
printGraph(mappa);
^
I can solve and compile successfully substituting:
for(GraphTraits<T>::node_iterator....
with:
for(GraphTraits<Map>::node_iterator....
But this makes printGraph
to loose it's generality.
Can someone give me some hint to solve the problem?
Talking in generale, is it possible to use a Traits to force a data-type definition? I mean:
template<typename T>
struct ATraits{
-> type "iterator" must be defined
};
such that every specialization has to define:
typedef ..something.. iterator;
Upvotes: 1
Views: 1606
Reputation: 283614
You forget to use the typename
keyword.
In GraphTraits<Map>::node_iterator
, the compiler knows what specialization of GraphTraits
is used, and can figure out that node_iterator
is a typedef.
In GraphTraits<T>::node_iterator
, it has no idea what specialization will be used, because T
isn't known yet. So it assumes that node_iterator
is a member variable, not a type. You have to correct this assumption. Say typename GraphTraits<T>::node_iterator
To require every specialization of GraphTraits
to provide a typedef, you'd need "concepts", which were proposed for C++11 but had to be delayed until a future version. So it isn't possible in C++ today. Whenever you actually USE printGraph
(formally, during instantiation of each template function instance of the printGraph<T>
function template), however, the compiler will double-check that GraphTraits<T>::node_iterator
really is a type (since now it knows T
and can perform overload resolution).
Upvotes: 4