Good way to template a class/struct combination - c++

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:
};
?

Related

How to use structs, typedef, and template?

I'm trying to implement the following code, but I keep getting an error because of typedef, can someone please help?
template<class T>
struct box{
T data;
box *link;
};
typedef box* boxPtr;
the error is:
Use of class template 'box' requires template arguments
box is a class template. This means that it needs an argument whenever you instantiate it:
box<int> integerBox;
box<float> floatBox;
// etc
This is required anywhere you try to use a template class such as box. So a pointer to just a box doesn't make sense. It needs a type with it. For example:
template <class type>
using boxptr = box<type>*;
This does effectively the same thing as your typedef, but it allows you to specify he underlying template type.
Of course, if you do it this way, you will always need to specify a type when you use the pointer version:
boxptr<int> integerBoxPtr;
boxptr<float> floatBoxPtr;
// etc
Writing
template<class T>
struct box{
T data;
box *link;
typedef box<T>* boxPtr;
};
with
int main()
{
box<int>::boxPtr use_of_a_bad_idea;
}
is one way, but having pointer types masquerading as object types is a recipe for memory leaks: don't do it as a rule of thumb. That's the best way of addressing this problem.
Use using:
template<class T>
struct box
{
T data;
box *link;
};
template <typename T>
using boxPtr = box<T>*;
Now boxPtr<int> is the same as box<int>*.
This will compile:
template<class T>
struct Box {
T data;
Box<T> *link;
};
typedef Box<int> * BoxPTR;

Template typename reuse when declared in member variables

Suppose I have the following code:
template<typename T>
struct Task
{
T* item;
Task* next;
int priority;
};
class PriorityQueue
{
public:
PriorityQueue();
Enqueue(T* item, int priority);
T* Dequeue();
private:
Task<T>* head;
};
The above code will throw a compilation error where T is not declared in the PriorityQueue class. If I would like to refer to the T* item which is defined as a template class in the Task struct, what would be the proper syntax to do so?
T does not have special meaning. Each template class or template method must be declared to be a template, using the syntax template<typename T> or variations of it. The two Ts will not automatically be the same in this case.
When you use a template class, you need to specify what the template arguments are, with the syntax classname<SomeType>. As #Antonio Garrido points out; in your case that likely means that your head member should be declared as Task<T>* head;
Now, as it seems that Task is not part of the interface, you can also declare it as part of the PriorityQueue class. That way T:
template<typename T>
class PriorityQueue
{
struct Task
{
T* item;
Task* next;
int priority;
}
public:
PriorityQueue();
Enqueue(T* item, int priority);
T* Dequeue();
private:
Task* head;
};
You then use the template class:
PriorotyQueue<int> que;
auto que = PriorotyQueue<int>(); // alternatively
que.Engueue(3);
I'm not sure that way 1 would work, unless you linked in something named T. and also, you need ; after your class declarations.
As far as whether the two T's are the same, they're not. The whole point of Templates is that it allows you to have two types that are "kinda" the same but have different types. they are only the same in that they have the same interface.
I could do something like this in one single program:
PriorityQueue<std::string> stringQueue;
PriorityQueue<int> intQueue;
and I would have two different types of queues. stringQueue is of type PriorityQueue<std::string> and intQueue is of type PriorityQueue<int>. these are actually different types that share the same interface, because the C++ compiler will compile two different binaries for each. if I did something like:
intQueue.enqueue(5, 0);
stringQueue.enqueue("hello world", 1);
then stringQueue would contain a single Task of type Task<string>, because when PriorityQueue<std::string>::enqueue makes a new Task it makes a Task<std::string and the same with intQueue and integers.
T is just a variable which is conventional, but you can also do templates like this:
template<typename Type>
template<typename A> //these two are basically the same as T
template<Task SpecialTask> //here SpecialTask would have to be a
//Task or a child of Task
template<class C> //C must be a class
template<typename T, U, V> //here are three templates in one declaration
template<PriorityQueue PQ,
Task T,
typename U> //three of different "lineage"
so How does the compiler know that a PriorityQueue<int> should have Task<int> attributes? well it happens when it notices that it has a template attribute, or it is newing a template, and then it goes and compiles up binaries for Task<int> if it didn't have any already.
The last line is
Task<T>* head;
By the way, you need to add ';' to end the struct/class definition

How to use templates with Nested class in C++

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).

C++ new instance of nested class

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;
}

Templates and nested classes/structures

I have a simple container :
template <class nodeType> list {
public:
struct node {
nodeType info;
node* next;
};
//...
};
Now, there is a function called _search which searches the list and returns a reference to the node which matched. Now, when I am referring to the return-type of the function, I think it should be list<nodeType>::node*. Is this right? When I define the function inline, it works perfectly:
template <class nodeType> list {
public:
struct node {
nodeType info;
node* next;
};
node* _search {
node* temp;
// search for the node
return temp;
}
};
But, if I define the function outside the class,
template <class nodeType> list<nodeType>::node* list<nodeType>::_search() {
//function
}
it doesn't work. The compiler gives an error saying Expected constructor before list<nodeType>::_search or something. The error is something similar to this. I don't have a machine on which I can test it currently.
Any help is sincerely appreciated.
that's because node is a dependent type. You need to write the signature as follows (note that I have broken it into 2 lines for clarity)
template <class nodeType>
typename list<nodeType>::node* list<nodeType>::_search()
{
//function
}
Note the use of the typename keyword.
You need to tell the compiler that node is a type using the keyword typename.Otherwise it will think node as a static variable in class list. Add typename whenever you use node as a type in your implementation of list.