Search code examples
c++c++11vectornew-operator

Why i get a SIGSEGV error on push_back(T&&) when T=std::string custom Vector C++


#include <algorithm>
#include <utility>
#include <new>
#include <iostream>

template <typename T>
class Vector {
public:
    Vector();
    ~Vector();

    void push_back(const T& value);
    void push_back(T&& value);

    void clear();

    std::size_t size() const { return sz; }
    std::size_t capacity() const { return cap; }

    T& operator[](std::size_t i) { return data[i]; }
    const T& operator[](std::size_t i) const { return data[i]; }

private:
    T* data;
    std::size_t sz;
    std::size_t cap;

    void p_realloc(std::size_t n);
};

template <typename T>
Vector<T>::Vector()
    : sz{}, cap{ 10 }
{
    data = (T*)::operator new(cap * sizeof(T));
}

template <typename T>
Vector<T>::~Vector() {
    clear();
    ::operator delete(data, sizeof(T) * cap);
}

template <typename T>
void Vector<T>::p_realloc(std::size_t n) {

    T* new_data = (T*)::operator new(n * sizeof(T));

    if (n < sz)
        sz = n;

    for (std::size_t i = 0; i < sz; ++i) {
        new_data[i] = std::move(data[i]);
        data[i].~T();
    }

    ::operator delete(data, cap * sizeof(T));

    data = new_data;
    cap = n;
}

template <typename T>
void Vector<T>::clear() {
    for (std::size_t i = 0; i < sz; ++i)
        data[i].~T();
    sz = 0;
}


template <typename T>
void Vector<T>::push_back(const T& value) {
    if (sz >= cap)
        p_realloc(cap * 1.5);
    data[sz++] = value;
}

template <typename T>
void Vector<T>::push_back(T&& value) {
    if (sz >= cap)
        p_realloc(cap * 1.5);
    
    std::cout << "All good\n"; std::cin.get();

    data[sz++] = std::move(value);
}

I am trying to create a custom implementation of Vector class. I have already made one but it does not use ::operator new and delete in order to not call the constructor/destructor. When T=std::string and i try to call push_back("test_string") i get an error and i cannot figure about why. Should it not be implicitly converted to std::string and therefore data[sz++] = std::move(value) work ?


Solution

  • The error is not in the using push_back(T&&). The reason is in the data that points to an uninitialized memory.

    data[sz++] = value; calls T::operator=(const T&) on an uninitialized object T.

    data[sz++] = std::move(value); calls T::operator=(T&&) on an uninitialized object T.

    You should fix assignments data[sz++] = in the both push_back with using the placement new:

    /* data[sz++] = value; */            new (&data[sz++]) T(value);
    /* data[sz++] = std::move(value); */ new (&data[sz++]) T(std::move(value));