Rich
Rich

Reputation: 71

Good way to template a class/struct combination

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.

Upvotes: 1

Views: 1738

Answers (2)

Tony Delroy
Tony Delroy

Reputation: 106196

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.

Upvotes: 2

LeleDumbo
LeleDumbo

Reputation: 9340

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

?

Upvotes: 0

Related Questions