czifro
czifro

Reputation: 784

C++ -- determine if generic variable is a pointer

I have a class, which I'll refer to as myclass, that has a list container of the type T. I also have a couple of methods that remove items from the list. Should the T be a pointer of some sort, I would like to check that it indeed is a pointer and then delete it in order to relieve allocated resources back to memory. Here's a snippet of code:

template<typename T>
class myclass{
private:
    std::list<T> * container;
    // other vars
public:

    void erase(const T &item){
        if (!this->find(item))     // find is defined elsewhere
            return false;
        auto temp = container->begin();
        for (int i = 0; i < container->size(); ++i){
            // this is where i would like to check if *temp is a pointer,
            // so that I can assign it to a pointer var, remove it from the list,
            // then delete the pointer,
            //otherwise just simply remove it from the list.
        }
    }

};

EDIT

auto temp = container->begin();

I want to know how to determine if *temp is a pointer so that I can do the following:

T * var = *temp;
container->remove(temp);   // remove or erase, i can't recall at the moment
delete var;

but I only want to do that if *temp is a pointer

Upvotes: 2

Views: 6755

Answers (4)

Jerry Coffin
Jerry Coffin

Reputation: 490098

Sorry, but no: std::list<T>::iterator (which is what begin() will return and therefore will be the type of temp) can't ever be a pointer. It must be a type that (at the very least) overloads pre- and post-increment and decrement to do linked list traversal so ++ will do something like pos = pos->next; and -- will to something like pos = pos->prev;.

If you're trying to figure out if *temp (which will be the same type as T) is a pointer, that's a whole different story. You basically have two routes. The one I'd prefer as a general rule would be to provide a specialization of your class for pointers:

template<typename T>
class myclass{
private:
    std::list<T> container;
    // other vars
public:

    void erase(const T &item){
        if (!container->find(item))     // find is defined elsewhere
            return false;
        auto temp = container->begin();
        for (int i = 0; i < container->size(); ++i){
            container.erase(temp);
        }
    }

};

template<class T>
class myclass <T *> {
private:
    std::list<T> container;
    // other vars
public:

    void erase(const T &item){
        if (!container->find(item))     // find is defined elsewhere
            return false;
        auto temp = container->begin();
        for (int i = 0; i < container->size(); ++i){
            delete *temp;
            container.erase(temp);
        }
    }
};

The biggest problem with this is that you may end up duplicating a fair amount between the base template and the specialization for pointers. There are a couple of ways of avoiding that. One is to use a base class that implements the common behavior, then derive the two specializations from that to provide the specialized behavior. Another would be to use some enable_if or SFINAE to enable different versions of the erase function depending on whether the contained type is something that can be dereferenced or not.

As an aside, you probably shouldn't have std::list<T> *container; -- it should probably be just std::list<T> container; (or, better still in most cases, std::vector<T> container;)

Upvotes: 2

user3125280
user3125280

Reputation: 2829

Isn't it annoying container's don't delete normal pointers? Well in C++ raw pointers don't actually own the object. There could be many pointers pointing to the same object. You need a unique pointer - stl provides one in c++11. When a unique_ptr is removed from the list, it will destroy the object it points to, so there is no need to complicate erase.

#include <list>
#include <memory>
#include <type_traits>

using namespace std;

template<typename t, bool b>
struct Selector {
    typedef list<T> container;
};

template<typename t>
struct Selector<t, true> {
    typedef list<unique_ptr<T> > container;
};

template<typename T>
class myclass{
private:
    Selector<T, is_pointer<T>::value>::container* container;
    // other vars
public:

    void erase(const T &item){
        if (!this->find(item))     // find is defined elsewhere
            return false;
        auto temp = container->begin();
        for (int i = 0; i < container->size(); ++i){
            // removing the unique_ptr delete's pointer
        }
    }
};

Upvotes: 0

NicholasM
NicholasM

Reputation: 4673

I don't think this is a wise idea. You don't know whether the user has provided pointers to data allocated on the stack, or to data that is managed in some other way (eg with smart pointers).

But to answer the question, look at

std::is_pointer<T>::value  // in type_traits header

http://en.cppreference.com/w/cpp/types/is_pointer

This is a C++11 feature.

Upvotes: 2

PaulMcKenzie
PaulMcKenzie

Reputation: 35440

1) Determine if Type is a pointer in a template function

2) How would you know if that pointer is pointing to dynamically allocated memory?

Upvotes: 2

Related Questions