corwin corwin - 2 months ago 12
C++ Question

convert pointer type in assigment

I have a class that has:

private:
uint32_t *data;


and the in one of the functions I'm doing:

void foo() {
data = new uint32_t[size];
}


and that is working fine. Now i wanted to make it a bit more flexible, so i wanted to make foo() a template:

template<typename T>
T foo() {
data = new T[size];
}


and i try to use it:

class.foo<uint64_t>();


But compilation fails saying that:

error: cannot convert 'long long unsigned int*' to 'unsigned int*' in assignment


Is it possible to do something like that? I tried declaring

void *data;


and it compiled, but then I cannot do

sizeof(data[1]);


which is essentially why I need to pass the type.

EDIT:

Thank you for your responses, there are a few thing I'd like to add after looking at your answers.


  • I'm using raw pointer instead of container because it operates on memory that is used also by external hardware (I'm not running this on PC).

  • Using this memory is optional for this class, so I don't want to allocate it if its not needed.

  • I use this class in a few places where I don't need *data at all, so I'd rather avoid making the whole class a template.



Another thought:
Default template type might be a good compromise, is there a way to create a class that I won't have to later use that way:

Class<> my;


but still:

Class my;


and if needed:

Class<type> my;


?

Answer

You may consider making your whole class a template, like this:

template <typename T>
class Foo
{
private:
    T *data;
public:
    Foo(size_t size_):
        data{new T[size]}
    {
    }
    ~Foo()
    {
        delete[] data;
    }
};

Implementation is here only partial. See SO documentation about rule of 3, 5, 0.

Or using managed pointers:

template <typename T>
class Foo
{
private:
    std::unique_ptr<T[]> data;
    size_t size;
public:
    Foo(size_t size_):
        data{std::make_unique<T[]>(size_)},
        size(size_)
    {
    }
    ~Foo()
    {
        // no need to call delete, unique_ptr will do it
    }
};

But once you're here, depending on your use case, alternatives may be preferable, like std::vector:

std::vector<uint64_t> v(size);
// ...
std::cout << v.size() << std::endl;

EDIT:

From the additional information you provided, it looks like the following design may better suit your needs:

class Base
{
public:
    virtual void* get_data() {
        return nullptr;
    }
    virtual size_t get_size() {
        return 0;
    }
};

template<typename T>
class Foo : public Base
{
private:
    T* data;
    size_t size;
public:
    Foo(size_t size_):
        data{new T[size_]},
        size(size_) {}
    ~Foo() {
        delete[] this->data; // same remark as above about rule of 5
    }
    virtual void* get_data() overriden {
        return this->data;
    }
    virtual size_t get_size() overriden {
        return this->size;
    }
};

With the following usage:

std::unique_ptr<Base> my_without_data =
    std::make_unique<Base>();

std::unique_ptr<Base> my_with_data =
    std::make_unique<Foo<type>>(size);

Note that in the second call, std::make_unique returns a unique_ptr<Foo<type>> with an appropriate deleter calling Foo<type>'s destructor. The deleter will be copied when assigning to my_with_data and call Foo<type>'s destructor even if Base's destructor is not declared virtual.

I chose here a design with virtual methods in Base to access data. Depending on your real use case, other ways may be used.

Comments