0

I need to implement the following class :

template <class Element, class Compare = std::equal_to<Element>>
class UniqueArray {
    Element* data;
    unsigned int size;
    unsigned int max_size;
public:
    explicit UniqueArray(unsigned int size);
    UniqueArray(const UniqueArray& other);
    ~UniqueArray();
    UniqueArray& operator=(const UniqueArray&) = delete;
    unsigned int insert(const Element& element);
    bool getIndex(const Element& element, unsigned int& index) const;
    const Element* operator[] (const Element& element) const;
    bool remove(const Element& element);
    unsigned int getCount() const;
    unsigned int getSize() const;
};

The problem is I can't assume that Element has a defualt constructor. Assuming I can't see the implementation of Element class meaning that there might be other constructors but I don't know how much parameters there are and what are their types. How can I initialize the data attribute of UniqueArray?

For example Element can be a Point which has a constuctor with two arguments(and no defualt constructor) But the point is I don't know which element is being sent and I don't know what constructor this Element has. The code supposed to be generic.

Eliran Turgeman
  • 1,526
  • 2
  • 16
  • 34

3 Answers3

1

It's unclear what the constructor UniqueArray(unsigned int size) is supposed to do - default-construct that many elements or reserve storage?

If it only reserves storage, then you could use std::aligned_storage to decouple storage allocation from object construction, and let the user of UniqueArray create the elements:

template <class Element, class Compare = std::equal_to<Element>>
class UniqueArray {
    using storage_type = typename std::aligned_storage<sizeof(Element), alignof(Element)>::type;
    storage_type* data;
    unsigned int size;
    unsigned int max_size;
public:
    explicit UniqueArray(unsigned int size) : size(0), max_size(size) {
        data = new storage_type[size];
    }
    ~UniqueArray() {
        for (unsigned pos = 0; pos < size; ++pos) {
            // note: needs std::launder as of C++17
            reinterpret_cast<Element*>(&data[pos])->~Element();
        }
        delete[] data;
    }
    Element& operator[](unsigned pos) {
        // note: needs std::launder as of C++17
        return *reinterpret_cast<Element*>(&data[pos]);
    }
    void insert(const Element& element) {
        new (&(*this)[size++]) Element(element); // placement-new
    }
    // ...
};

struct A {
    int a, b;
    A(int x) : a(x), b(x + 5) {}
};
int main() {
    UniqueArray<A> arr(3);
    arr.insert({ 2 });
    std::cout << arr[0].b << std::endl; // prints "7"
}
rustyx
  • 80,671
  • 25
  • 200
  • 267
1

First, gathering requirements:

  • "std:equal_to is the only thing I can use from the standard library"
  • There's a max_size and a size member, getCount() returns the current size and getSize() the max size (side note: this is horrible ...)

So I'd assume that the constructor of your class should allocate enough memory to hold size (the parameter) objects of the Element class.

The proper way to do this would be - as suggested in rustyx' answer - to use std::aligned_storage. Since this is for a university homework and your lecturer / professor / person who wrote this assignment probably doesn't know enough about C++ to care / understand the issues you probably are best of with something like this:

// in constructor, I repeat, THIS IS HORRIBLE!
max_size = size; // better use member initialization, though
data = reinterpret_cast<Element*>(new unsigned char[sizeof(Element) * max_size]);

When inserting you use placement new to create a new element as a copy:

new (data[index_for_new_element]) Element(element);

For erasing you need to call the destructor manually!

data[index_to_erase].~Element();

I repeat: This is not how you'd write such code in a real application! It does not teach you anything valuable!

Daniel Jour
  • 15,896
  • 2
  • 36
  • 63
0

After a discussion with a tutor this is the way to go about it :

data member variable will be initialized to an array of null pointers. each pointer points to an Element.

We can assume that there is a copy constructor. Whenever we want to insert a new element to the array we call insert() and this function inserts a copy of the element it got as an argument.

Eliran Turgeman
  • 1,526
  • 2
  • 16
  • 34