I'm trying to develop 2 classes, Node and Connection, but I don't have experience in C++ or C++ templates.
The Node contains a list of connections and a Connection contains 2 nodes.
So I suppose that a node has a template parameter that specifies which type of connections are in the list and that a connection has a template parameter that specifies which kind of nodes it contains.
How can I enforce in C++ that the node contains connections of a generic type but that these connections contain nodes of the class Node? The same question for the Connection class. I want to have a generic parameter for the type of the nodes, but these generic nodes must contain a list with connections of the Connection class.
I've tried several things, this is what I have at the moment:
template <template <template <class Conn> class Node> class Conn>
class Node {
};
Can someone help me?
Thanks in advance,
Jef
Assuming that you want different types of Nodes, but that Connections are nothing but a link between two Nodes (that is, that you don't need to do any specialisation on the Connections) then you could do something like:
template <class Node>
class Connection
{
Node& node1;
Node& node2;
};
template <class Node>
class NodeBase
{
std::list< Connection<Node> > connections;
};
// example concrete node
class MassNode : public NodeBase<MassNode>
{
// stuff that makes a mass node more than just a node.
}
This is a pattern called the curiously recurring template pattern.
There are other ways of attacking this - can you give more info on your specific problem domain?
EDIT to show intrusive vs non-intrusive techniques
namespace intrusive
{
template <class node>
class directedConnection
{
node& From;
node& To;
};
template <class node>
class directedGraphNode
{
private:
std::set< directedConnection<node>* > OutgoingConnections;
std::set< directedConnection<node>* > IncomingConnections;
};
// sample concrete class. Note that it is a graph node AND it contains the node data.
class bayesianNetworkNode : public directedGraphNode<bayesianNetworkNode>
{
public:
double Probabilities[16];
};
bayesianNetworkNode B1, B2, B3;
}
namespace non_intrusive
{
template <class T>
class undirectedGraphNode;
template <class T>
class undirectedConnection
{
undirectedGraphNode<typename T>& Node1;
undirectedGraphNode<typename T>& Node2;
};
template <class T>
class undirectedGraphNode
{
private:
std::set< undirectedConnection<T>* > Connections;
T Value;
public:
T& operator * () { return Value; }
T* operator -> () { return &Value; }
};
// sample concrete class. Note that this class contains the node data, but is NOT actually a graph node itself.
// It is "pointed to" by a node in the same way that an STL iterator "points to" a collection item.
class markovNetworkNode
{
public:
std::set<formula> Formulae;
};
undirectedGraphNode<markovNetworkNode> M1, M2, M3;
}
Related
I have some graph classes that can inherit from each other to provide the functionality needed in different cases (directed graph, traversable graph, nested graph, etc).
In order to make it possible to use the final derived types, these are provided as template arguments to the base classes. This way, for instance Edge::GetSource can return the derived class Node, and not the base class Node.
To make things simple, the graph classes take only one template argument: GraphTypes. GraphTypes in turn takes as template arguments all the needed derived graph classes. This makes it very easy to make changes to the types used in the graph classes, as only GraphTypes need to be changed.
template<class GraphT, class NodeT, class EdgeT>
class GraphTypes {
public:
using GraphType = GraphT;
using NodeType = NodeT;
using EdgeType = EdgeT;
}; // GraphTypes
template<class GraphTypes>
class Edge {
private:
using GraphType = typename GraphTypes::GraphType;
using NodeType = typename GraphTypes::NodeType;
using EdgeType = typename GraphTypes::EdgeType;
public:
NodeType &GetSource();
protected:
NodeType *m_pSource;
}; // Edge
template<class GraphTypes>
inline NodeType &Edge<GraphTypes>::GetSource() {
return *m_pSource;
} // Edge::GetSource
This is working great! But now I want to add an optional Context argument. In some cases, a context object needs to passed around while working on the graph and be made aware, for instance, when Edge::SetSource is called.
My idea is to add a variadic template argument. This way graph classes that need no context can just omit it. But how can I add a variadic template argument to GraphTypes and then use it inside the other graph classes that have GraphTypes as an template argument?
template<class GraphT, class NodeT, class EdgeT, class ...ContextT>
class GraphTypes {
public:
// error C3520: 'ContextT': parameter pack must be expanded in this context
using ContextType = ContextT;
}; // GraphTypes
template<class GraphTypes>
class Edge {
private:
using ContextType = typename GraphTypes::ContextType;
public:
// error C3543: 'GraphTypes::ContextType': does not contain a parameter pack
void SetSource(NodeType &Source, ContextType &...Context);
}; // Edge
template<class GraphTypes>
void Edge<GraphTypes>::SetSource(NodeType &Source, ContextType &...Context) {
if(&Source == m_pSource)
return;
// I think this should work, but open to suggestions ;)
std::initializer_list<bool>{ (Context.PreSetSource(*this), true)... };
m_pSource = &Source;
std::initializer_list<bool>{ (Context.PostSetSource(*this), true)... };
} // Edge::SetSource
I am trying to use templates for a nested class. I am not sure how to access the class type of the inner class from another class.
Sample Code below.
// I have a List class that can accept any type. It has an inner class
template <class T>
class List
{
public:
class Node
{
public:
T data;
Node* next;
Node* prev;
};
void addElement(Node& value);
private:
Node* head;
};
// Here I am making an array of Lists
template <class T>
class ListArray
{
public:
// Here is my question.
void add(Node& value); // How to give "Node" class type here ?
private:
List<T> _listArr[10];
};
// Is the below the right way to define ListArray::add, especially the way in which Node type can be passed to it ?
template <class T>
void ListArray<T>::add(List<T>::Node& value)
{
// Make a call to List::addElement and pass Node& value
_listArr[0].addElement(value);
//....
}
Could you kindly let me know how the above can be achieved ? Thanks.
Node is a nested type of a class template:
template <class T>
class ListArray
{
public:
typedef typename List<T>::Node Node_type;
void add(Node_type& value); // Refer to it as Node_type
private:
List<T> _listArr[10];
};
And:
template <class T>
void ListArray<T>::add(typename ListArray<T>::Node_type& value)
{
_listArr[0].addElement(value);
//....
}
I used typedef to define local name for node type. It is very useful - now, clients of ListArray can write code, that uses Node_type explicitly (without knowing what it actually is). This technique is used heavily in std library - usually, std:: types have tons of typedefs to allow writing flexible code.
Also, note the typename keyword - it is required in case of nested types of class templates. It indicates, that given name is the name of a type (without it, you should get a compiler error).
what's the most effective way to template a class that calls will need to call a templated struct. This is the classic ordered list problem with templating. My entire ordered list works as of now (when I simply change the types manually). However, I am not sure how to go about templating the pair of objects (the struct and the class).
So basically, here is how my code is structured:
struct Node {
int* data;
Node* next;
};
class OList {
private:
Node* start;
int size;
public:
a bunch of manipulation functions
};
So, my desire is to simply template the struct, and then accept a parameter that will pass the template into the Node struct type. However, my first attempt, which was to do:
template<class T>
before the Node struct and change all the ints* to T* failed miserably. What might be a better approach in anybody's experience? Can anybody point me in the right direction or give me some good references for template basics? All I can find are specific questions, which doesn't give me a good background to how templating works.
UPDATE: My code works very well at this point. The only thing I still do not understand is how to return pointers of the Node struct in functions. For example, in a function that might be,
template <class T>
List<T>::Node<T>* List<T>pos(int val); //trying to return a pointer to the node at a specified spot
I get the following error: "Non-templated 'Node' used as template. note: use OList::template Node' to indicate that it is a template (???) error: need 'typename' before 'OList::Node' because 'OList is a dependent scope" What is the most efficient way to clear up these errors? Code works perfectly when this one function is commented out.
template <typename T> // <----
struct Node {
T* data; // <----
Node* next;
};
template <typename T> // <----
class OList {
private:
Node<T>* start; // <----
int size;
public:
a bunch of manipulation functions
};
Alternatively...
template <typename T>
class OList {
private:
typedef ::Node<T> Node; // <---- just do it once
Node* start;
...or as suggested in BWG's comment, define Node directly in OList, so all the <T> aspect is implicit...
template <typename T>
class OList {
private:
struct Node { T* data; int size; }; // <----
Node* start;
Example of out-of-line member function definition:
template <typename T>
class OList
{
private:
struct Node { T* data; };
Node* f(Node*);
public:
};
template <typename T>
typename OList<T>::Node* OList<T>::f(typename OList<T>::Node* p) // see notes
{
Node* p2 = p; // can use Node as if inside class definition
return p2;
}
Note the ugly line...
typename OList<T>::Node* OList<T>::f(typename OList<T>::Node* p)
...where typename is needed to indicate that Node names a type inside QList<T> (so it can do a bit more to make sure the function might makes sense even before it's instantiated for any specific type 'T'), and we need to continually mention both that Node's inside QList's scope, and that the specific instantiation of QList in which it's to be found is based on the template parameter T. It all makes sense, but it's a bit pedantic.
As for how templates work in general, that's arguably "too broad" for answers on Stack Overflow, but FWIW perhaps the fastest way to jump-start some practical understanding of that (which will need some refinement afterwards), is by comparing them to macros. Consider:
#define NODE(T) \
struct Node ## T { \
T* data; \
Node ## T* next; \
};
With this, you can say NODE(int) or NODE(float) to generate Node_int and Node_float structures for those types. With templates, you don't (normally) need to define each specialisation separately - it's done implicitly when used ("parametric polymorphism")- so just start using them for variables ala Node<int> my_node_for_ints.
Do you mean something like:
template <typename T>
struct Node {
T data;
Node* next;
};
template <typename T>
class OList {
private:
Node<T> start;
int size;
public:
};
?
Here is my code
template <typename T>
struct Item
{
T* Product;
Item *next;
Item(T*);
};
template <typename T>
Item<T>::Item(T *prod)
{
this->Product = prod;
this->next = nullptr;
}
template <typename T>
class Catalog
{
private:
Item<T> *first;
Item<T> *last;
unsigned int count;
public:
struct Item; //i need Item as nested structure
void Add(T*);
};
template <typename T>
void Catalog<T>::Add(T *ptr)
{
//only for simplification, algorithm I already have
this->first = new Item<T>(ptr);
//or
this->last = new Item<T>(ptr);
}
I do not know how add new Item to my class Catalog. If Item is not nested struct, I can use simply new Item<T>(ptr)
Is any way how to do this?? It is my project to school and I need Item as nested struct.
Thank you, sorry about my English.
The problem is that you're declaring two different Item. The
first is a non-nested template struct, which you (probably)
don't want to use, and the second is an incomplete nessted
struct. The second is the one you probably want to use, but
it is incomplete, so you cannot create an instance of it. What
you should do is put the entire definition of the Item in the
class Catalog, instead of just the forward definition. (And
don't define it there as a template: the type you want is
Catalog<T>::Item, and not Catalog<T>:Item<U>.) Then, of
course, inside the class template, it is new Item (and not new
Item<T>); outside the class, it is new Catalog<T>::Item.
You must put the definition inside the definition of the class:
template <typename T>
class Catalog
{
public:
struct Item
{
T* Product;
Item *next;
Item(T*);
};
void Add(T*);
private:
Item *first;
Item *last;
unsigned int count;
};
Note that now the struct's type is Catalog<T>::Item, so elsewhere you will have to use that instead of just Item<T>:
template <typename T>
Catalog<T>::Item::Item(T *prod)
{
this->Product = prod;
this->next = nullptr;
}
I have a templated node class declared as
template <class T_>
class Node : public std::iterator<std::bidirectional_iterator_tag, T_, T_>
{
...
}
Which works fine, and I'm trying to take this existing class and apply it to a new linked list class.
I'd like to create it by calling something along the lines of
LinkedList<float> nodeList;
where it implicitly takes the type given, and within the structure of the class handles it as Node of type T_.
Is there a way to do this without making the node a sub-class of LinkedList?
Is this what you are asking?
template <class T_>
class LinkedList
{
public:
typedef Node<T_> node_type;
void AddNode(node_type *node)
{ /*...*/ }
};
Technically, the typedef is not necessary, but IME it is a good pratice to typedef the dependent types in this way.
You can provide the iterator as a default template argument:
template<
class T,
class Iterator = Node<T>
> class LinkedList
{
// ...
};
If i understood your question correct, you need to instantiate a class template using templated class type. Use this syntax:
LinkedList<Node<float> > nodeList;
(notice a space between > >; it is always a good idea to add it, because old versions of some compilers have troubles on distinguishing such constructs from shift operators >>)