2

I hava a little problem while coding a template class win C++. The problem is fairly simple: I dn't know whether to do a delete on the paremetrized type or not, since it could, or it could not, be a pointer.

I have seen this: Destructor in template class c : How to delete field which may be pointer or not pointer?

and I have implemented the first solution, but this requires me to specialize the whole class, so it means I must have 2 classes:

template<class T>
class Node {
private:
    T _content;
public:
    Node(const T c); 
    ~Node();
};

and

template<class T>
class Node<T*> {
private:
    T _content;
public:
    Node(const T c); 
    ~Node();
};

I would like to have only the second version and specialise only the destructor as follows:

template<class T>
class Node<T*> {
private:
    T _content;
public:
    Node(const T c); 
    ~Node();
};
template <class T>
Node<T>::~Node() {
    while(!_adjacent.isEmpty()) {
        disconnectFrom(_adjacent.first());
    }
}
template <class T>
Node<T*>::~Node() {
    while(!_adjacent.isEmpty()) {
        disconnectFrom(_adjacent.first());
    }
    delete _content;
}

but then I get the following error:

Node.hpp:43:17: error: invalid use of incomplete type ‘class Node<T*>’
Node.hpp:8:7: error: declaration of ‘class Node<T*>’

Is there any way to specialise only the constructor to avoid having 2 classes (my Node class is much bigger than what I show here)?

Thanks!

Community
  • 1
  • 1
excalibur1491
  • 1,201
  • 2
  • 14
  • 29
  • Could you explain more clearly what method would you like to specialize? And no, you can't partially specialize function templates and methods of class templates (constructors and destructors are methods, so...). – Constructor Apr 03 '14 at 20:10
  • why not use unique_pointer instead of raw pointer? No overhead, and it will do as you want. – Deduplicator Apr 03 '14 at 20:26

3 Answers3

5

A solution would be to use a traits class:

template<typename T> struct delete_traits
{
  void destroy(T&) {}
};

template<typename T> struct delete_traits<T*>
{
  void destroy(T* p) { delete p; }
};

and then in your class destructor write

delete_traits<T>::destroy(_contents);

Besides not having to specialize the Node template, it has the additional advantage that you can easily can add other ways to destroy things without ever touching the file you defined Node in, by simply adding another template specialization:

// assumes that mylib_handle is a truly different type, maybe a C struct
// from the C interface of a library
template<> struct delete_traits<mylib_handle>
{
  void destroy(mylib_handle& h) { mylib_handle_free(m); }
};
celtschk
  • 19,311
  • 3
  • 39
  • 64
  • Bad idea, because now Node **always** owns whatever is behind `T`. No need for that. Also, how would you differentiate between char* allocated usin new, new[], malloc, SysString and the like? – Deduplicator Apr 03 '14 at 20:27
  • 1
    It's the functionality that was requested. Whether that is a bad idea you better discuss there. I just showed how to implement it. – celtschk Apr 03 '14 at 20:32
  • As far as I understand, a specific functionality was requested: Being able to specialize the template for owning / non owning use. Also, a partial implementation of the questioners idea was shown, and that it didn't work as wanted. Pointing out that he goes about it wrong is imho right there. – Deduplicator Apr 03 '14 at 20:37
  • 3
    Well, I read the request the way I answered it. Anyway, pointing out that it's a bad thing to want is certainly a good idea, if done to the right person (I upvoted your reply to the question for this reason). However saying how you can do it if you want it is IMHO also a valid thing to do, especially since the technique is general and not restricted to this specific problem. – celtschk Apr 03 '14 at 20:41
  • YES. YES! This is, surely, not a perfect solution - all sorts of trouble you might run into - but it's working for me for my specific use case, and I am immensely grateful for it. THANK YOU. – Helpful Aug 08 '18 at 02:25
4

It seems that you want this:

template<class T>
class Node {
private:
    T _content;
public:
    Node(const T c) : _content { c } {}
    ~Node() {}
};

template<class T>
class Node<T*> {
private:
    T* _content;
public:
    Node(const T* c) : _content { c } {}
    ~Node() { delete _content; }
};

And, as @Constructor said, no, you can't just specialize the destructor, you have to do the whole class... But if the class is bigger, you can refactor the commonalities to a base class and you'd have something like:

template<class T>
class BaseNode {
private:
  template<class> friend class Node;
    T _content;
public:
    BaseNode(T c) : _content { c } {}
    ~BaseNode() {}
};

template<class T>
struct Node : BaseNode<T> {
  Node(T c) : BaseNode<T>(c) {};
  ~Node() {}
};

template<class T>
struct Node<T*> : BaseNode<T*> {
  Node(T* c) : BaseNode<T*>(c) {};
  ~Node() { delete BaseNode<T*>::_content; }
};
Massa
  • 8,647
  • 2
  • 25
  • 26
4

Don't specialize Node at all. Just use the proper type for your intents:

  • Sole ownership: std::unique_pointer<T>.
  • Shared ownership: std::shared_pointer<T>.
    • Backlink for shared ownership: std::weak_pointer<T>.
  • No ownership: raw pointer

If you need a specific destructor, those templates have a parameter for that.
Also, you can use them for handles and the like, even though they aren't named pointer.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118