I am writing a Graph class and I want to provide multiple types of graphs with the same class. More specifically, I want to consider directed or undirected graphs, as well as the possibility for vertices (and edges) to have values.
Right now, my class looks something like that.
template<typename VertexValue = void, typename EdgeValue = void, bool directed = false>
class Graph {
public:
struct Vertex;
using Edge = Vertex::Edge;
Graph() = default;
//void addVertex(const VertexValue& vertex);
private:
std::vector<Vertex> m_vertices;
};
For convenience, I will skip anything related to edges implementation.
What I want to do now is to "specialize" the Vertex class to provide - or not - a value, and different methods depending on whether the graph is directed or not. I would like to be able to do something like the following (note : Vertex methods are only for illustrating purposes, directed and undirected versions of Vertex having different methods).
template<typename VertexValue = void, bool directed = false>
class Graph {
public:
struct Vertex;
private:
std::vector<Vertex> m_vertices;
};
// Directed, with value
template<typename VertexValue, bool directed>
struct Graph<VertexValue, directed>::Vertex {
using ValueType = VertexValue;
EdgeIterator inEdges();
EdgeIterator outEdges();
ValueType value;
};
// Directed, without value
template<bool directed>
struct Graph<void, directed>::Vertex {
using ValueType = void;
EdgeIterator inEdges();
EdgeIterator outEdges();
};
// Undirected, with value
template<typename VertexValue>
struct Graph<VertexValue, false>::Vertex {
using ValueType = VertexValue;
EdgeIterator edges();
ValueType value;
};
// Undirected, without value
template<>
struct Graph<void, false>::Vertex {
using ValueType = void;
EdgeIterator edges();
};
Unfortunately, I would have to specialize the whole Graph class in order to do that. So I tried something else : having the Vertex class being a template, and forwarding the outer template arguments.
template<typename VertexValue = void, bool directed = false>
class Graph {
public:
template<typename Value, bool, typename dummy = void>
struct Vertex;
private:
std::vector<Vertex<VertexValue, directed>> m_vertices;
};
// Directed, with value
template<typename VertexValue, bool directed>
template<typename Value, bool, typename dummy>
struct Graph<VertexValue, directed>::Vertex {
using ValueType = Value;
EdgeIterator inEdges();
EdgeIterator outEdges();
ValueType value;
};
// Directed, without value
template<typename VertexValue, bool directed>
template<bool directed_, typename dummy>
struct Graph<VertexValue, directed>::Vertex<void, directed_, dummy> {
using ValueType = void;
EdgeIterator inEdges();
EdgeIterator outEdges();
};
// Undirected, with value
template<typename VertexValue, bool directed>
template<typename Value, typename dummy>
struct Graph<VertexValue, directed>::Vertex<Value, false, dummy> {
using ValueType = Value;
EdgeIterator edges();
ValueType value;
};
// Undirected, without value
template<typename VertexValue, bool directed>
template<typename dummy>
struct Graph<VertexValue, directed>::Vertex<void, false, dummy> {
using ValueType = void;
EdgeIterator edges();
};
This would work ; however, I do not like the idea of forwarding the template arguments, and it also introduces a burden, the dummy template parameter. I have also thought about defining the Vertex class outside of the Graph class, like so :
template<typename ValueType, bool directed>
class GraphVertex {
EdgeIterator inEdges();
EdgeIterator outEdges();
ValueType value;
};
// Specializations...
template<typename Vertex = GraphVertex<void, false>>
class Graph {
public:
// ...
private:
std::vector<Vertex> m_vertices;
};
...or even like that :
template<typename ValueType, bool directed>
class GraphVertex {
EdgeIterator inEdges();
EdgeIterator outEdges();
ValueType value;
};
// Specializations...
template<typename VertexValue = void, bool directed = false>
class Graph {
public:
// ...
private:
std::vector<GraphVertex<VertexValue, directed>> m_vertices;
};
...but I prefer the idea of nested class, where the Vertex class really is a property of the Graph class. Plus, even if this solution, out of the three, would still have my preference, I do not find any other name for "GraphVertex". And it bothers me.
In the end, here is my question. Is there any other way to do what I want (namely, provide different Vertex classes - or features - depending on the Graph template arguments) ? If so, how to and what would be the advantages ; if not, what should I prefer and why ?
Aucun commentaire:
Enregistrer un commentaire