1

I'm new to C++ and I have the ambition to understand how templates work. So I have implemented a generic list MyList which may contain both built-in primitive types and pointers. In the remove function I want to distinguish pointer types and built-ins so I can delete the object behind the pointer but leave the built-ins untouched.

To distinguish the template types, which can be pointers or non-pointers, I wrote the following functions, which work fine:

// distinguish between pointer and non-pointer type of template variable
template<typename T> bool is_pointer(T t) {
    return false;
}

template<typename T> bool is_pointer(T* t) {
    return true;
}

In the list function remove the idea was to test for pointers and delete them in case. However, the delete statement does not compile:

template<typename T> void MyList<T>::remove() {
    ...
    if (is_pointer(temp->getContent())) {
        // delete object pointer points to
        T t = temp->getContent();
        cout << t;    // prints out address
        // delete t;  // produces compiler error (see below)
}

In the main.cpp I test the list class with various types, I call amongst others:

MyList<int> mylist;                // with type int
mylist.remove();
mylist.add(3);
// add and remove elements

MyList<string> mylist2;           // with type string
...

MyList<string*> mylist3;          // with type string*
mylist.add(new string("three")); 
mylist.remove();

When I comment out the statement delete t; I can verify that the control flow is correct: the if-statement is only entered for the string* example. However, if I uncomment the delete statement the compiler complains like that:

../mylist.h: In member function ‘void MyList<T>::remove() [with T = int]’:
../main.cpp:36:18:   instantiated from here
../mylist.h:124:6: error: type ‘int’ argument given to ‘delete’, expected pointer
../mylist.h: In member function ‘void MyList<T>::remove() [with T = std::basic_string<char>]’:
../main.cpp:71:18:   instantiated from here
../mylist.h:124:6: error: type ‘struct std::basic_string<char>’ argument given to ‘delete’, expected pointer
make: *** [main.o] Error 1

What is it that I don't see? I'm using the delete statement on pointers only but still I get these compiler errors. If I print out t in the if-statement it is a pointer address!

Thomas789
  • 79
  • 1
  • 8
  • 1
    `T t` is not a pointer, what does `getContent()` return? Also, you are using the `void*` overload for `operator<<` which is why you are seeing the address. – Jesse Good Sep 19 '13 at 09:46
  • 1
    Of course, you can't delete non-pointer types (that have no implicit conversion to an object pointer type). You can use a function `destroy` that has overloads for pointer and non-pointer types just like your `is_pointer`. – dyp Sep 19 '13 at 09:47
  • 1
    T t is not a pointer, so you cant delete it – shofee Sep 19 '13 at 09:48
  • (However, ) It might not be a good idea to store raw pointers and delete them later on, as you can easily form pointers to variables of automatic, static (or thread) storage duration. Just provide the non-pointer version and let the users use smart pointers instead. – dyp Sep 19 '13 at 09:48
  • 1
    Do you expect `if (false) delete 42;` to compile? That's essentially what you have. – Henrik Sep 19 '13 at 09:49
  • `getContent()` returns a variable of type `T`. – Thomas789 Sep 19 '13 at 09:54
  • Use smart pointers and avoid this headache-prone freakness – Manu343726 Sep 19 '13 at 10:56

3 Answers3

1

A template is a blueprint that the compiler uses to actually create types based on the use of the blueprint. When you use you template with int and string* the compiler will actually create two variations of MyList, replacing T with the actual type. The implementation that uses int for T is bogus, because deleting an int does not make sense. The actual code the compiler generates is

int t = temp->getContent();
cout << t;
delete t;

This is incorrect, as you could figure out.

Marius Bancila
  • 16,053
  • 9
  • 49
  • 91
  • I see, so it's not the compilation with the `string*` pointer that causes the compiler to complain but the compilation with the `int` and `string` types – Thomas789 Sep 19 '13 at 09:57
  • 1
    @DyP So what I could use would be two `destroy` functions along the lines of the `is_pointer` functions with one that does nothing in case of built-ins and another that calls delete in case of pointers. – Thomas789 Sep 19 '13 at 10:02
  • @Thomas789 Exactly, though, as I wrote in another comment, it might be best not do distinguish between them but rather let the user of the class use smart pointers instead. This also allows them to create `MyList`s of unmanaged raw pointers (for objects that are managed elsewhere). – dyp Sep 19 '13 at 10:19
  • @DyP I don't quite understand. The users should create smart pointers of the raw pointers but should supply the raw pointers to the list? But if the smart pointers are out of scope the objects will be deleted although the list still contains pointers to them? – Thomas789 Sep 19 '13 at 10:28
  • 1
    @Thomas789 What I meant was that the users should have the choice to either use `MyList>` if `MyList` is to "manage" the lifetime of the stored `some_type` objects, and `MyList` if `MyList` shall not manage the lifetime of the pointed-to objects. – dyp Sep 19 '13 at 10:31
  • Yup: I'd hate it if `MyList` would try to call `delete` on a string literal. Even if you knew that a pointer should be deleted, you wouldn't know whether to do that with `delete` or `delete[]`. So, just destroy each element of the list, and let its destructor handle anything else that's necessary. – MSalters Sep 19 '13 at 11:14
0

If you instantiate the template with T = int, you get:

void MyList::remove() {
    if (false) {
        T t = temp->getContent();
        cout << t;
        delete t;
    }
}

Even if the code block is never executed, it must be syntactically correct.

Henrik
  • 23,186
  • 6
  • 42
  • 92
0

I suggest to use C++ 11 type_traits and auto_ptr like this

#include <type_traits>
template<typename T> void MyList<T>::remove() {
    ...
    if (is_pointer(T)) {
        // delete object pointer points to
        T t = temp->getContent();
        cout << t;    // prints out address
        auto_ptr tempobj(T);
}

Also look at this Determine if Type is a pointer in a template function that could be useful incase your compiler is not C++ 11 compliant.

Thanks Niraj Rathi

Community
  • 1
  • 1
anonymous
  • 1,920
  • 2
  • 20
  • 30