0

I am doing a project for class and I think I have led myself into confusion following the book.

We are building a custom vector class with templates, which will go through a series of tests.

With my current code, it is failing when initializing the following statement.

Vector<int> v {1,2,3};

Here are my files.

vector.hpp

#ifndef VECTOR_HPP
#define VECTOR_HPP

#include <memory>

template<typename T, typename A = std::allocator<T> >
struct Vector_base {
    A alloc;
    T* elem;
    T* space;
    T* last;

    Vector_base(const A& a, typename A::size_type n) : alloc{a}, elem{alloc.allocate(n)}, space{elem+n}, last{elem+n} { }
    ~Vector_base() { alloc.deallocate(elem,last-elem); }
    Vector_base(const Vector_base&) = delete;

    Vector_base& operator=(const Vector_base&) = delete;
    Vector_base(Vector_base&&);
    Vector_base& operator=(Vector_base&&);
};

template<typename T, typename A = std::allocator<T> >
class Vector {
    Vector_base<T,A> vb;
    void destroy_elements();
public:
    using size_type = typename A::size_type;

    bool is_empty() const
    {
        return this->empty();
    }

    explicit Vector(size_type n, const T& val = T(), const A& = A());

    Vector() {}

    Vector(const Vector& a);
    Vector& operator=(const Vector& a);

    Vector(Vector&& a);
    Vector& operator=(Vector&& a);

    ~Vector() { destroy_elements(); }

    size_type size() const { return vb.space-vb.elem; }
    size_type capacity() const { return vb.last-vb.elem; }

    void reserve(size_type);

    void resize(size_type, const T& = {});
    void clear() { resize(0); }
    void push_back(const T&); 
};

#endif

vector.cpp

#include "vector.hpp"
#include <memory>

//template<typename T, typename A>
Vector_base<T,A>::Vector_base(Vector_base&& a) : alloc{a.alloc}, elem{a.elem}, space{a.space}, last{a.space}
{
    a.elem = a.space = a.last = nullptr;
}

//template<typename T, typename A>
Vector_base<T,A>& Vector_base<T,A>::operator=(Vector_base&& a)
{
    swap(*this,a);
    return *this;
}

//template<typename T, typename A>
void Vector<T,A>::destroy_elements()
{
    for (T* p = vb.elem; p!=vb.space; ++p)
        p-> ~T();

    vb.space=vb.elem;
}

//template<typename For, typename T>
void uninitialized_fill(For beg, For end, const T& x)
{
    For p;
    try {
        for (p=beg; p!=end; ++p)
            ::new(static_cast<void*>(&*p)) T(x);
    }
    catch (...) {
        for (For q = beg; q!=p; ++q)
            (&*q)-> ~T();
        throw;
    }
}

//template<typename T, typename A>
Vector<T,A>::Vector(size_type n, const T& val, const A& a) : vb{a,n}
{
    uninitialized_fill(vb.elem,vb.elem+n,val);
}


//template<typename T, typename A>
Vector<T,A>::Vector(const Vector<T,A>& a) : vb{a.alloc,a.size()}
{
    std::uninitialized_copy(a.begin(),a.end(),vb.elem);
}

//template<typename T, typename A>
Vector<T,A>::Vector(Vector<T,A>&& a) : vb{move(a.vb)} { }

/*template<typename T, typename A>
Vector<T,A>::Vector(const Vector& a)
{

}*/

//template<typename T, typename A>
Vector<T,A>& Vector<T,A>::operator=(Vector<T,A>&& a)
{
    clear();
    swap(vb,a.vb);
    return *this;
}

//template<typename T, typename A>
Vector<T,A>& Vector<T,A>::operator=(const Vector& a)
{
    if(capacity() < a.size())
    {
        Vector temp {a};
        swap(*this,temp);
        return *this;
    }

    if (this == &a) { return *this; }

    size_type sz = size();
    size_type asz = a.size();
    vb.alloc = a.vb.alloc;
    if (asz<=sz) 
    {
        copy(a.begin(),a.begin()+sz,vb.elem);
        for(T* p = vb.elem+asz; p!=vb.space; ++p)
            p-> ~T();
    }
    else
    {
        copy(a.begin(),a.begin()+sz,vb.elem);
        uninitialized_copy(a.begin()+sz,a.end(),vb.space);
    }
    vb.space = vb.elem+asz;
    return *this;
}

//template<typename T, typename A>
void safe_assign(Vector<T,A>& a, Vector<T,A> b)
{
    swap(a,b);
}

//template<typename T, typename A>
void Vector<T,A>::reserve(size_type newalloc)
{
    if (newalloc<=capacity()) return;
    Vector_base<T,A> b {vb.alloc,newalloc-size()};
    uninitialized_move(vb.elem,vb.elem+size(),b.elem);
    swap(vb,b);
}

//template<typename In, typename Out>
Out uninitialized_move(In b, In e, Out oo)
{
    using T = typename std::iterator_traits<Out>::value_type;
    for (; b!=e; ++b,++oo) {
        new(static_cast<void*>(&*oo)) T{move(*b)};
        b-> ~T();
    }
    return oo;
}

//template<typename T, typename A>
void Vector<T,A>::resize(size_type newsize, const T& val)
{
    reserve(newsize);
    if (size()<newsize)
        uninitialized_fill(vb.elem+size(),vb.elem+newsize,val);
    else
        destroy(vb.elem+newsize,vb.elem+size());
    vb.space = vb.elem+newsize;
}

//template<typename In>
void destroy(In b, In e)
{
    using T = typename std::iterator_traits<In>::value_type;
    for (; b!=e; ++b)
        b-> ~T();
}

//template<typename T, typename A>
void Vector<T,A>::push_back(const T& val)
{
    if (capacity()==size())
        reserve(size()?2*size():8);
    vb.alloc.construct(&vb.elem[size()],val);
    ++vb.space;
}

What am I missing here?

Clix
  • 5
  • 3
  • 2
    You need a constructor taking [`std::initializer_list`](http://en.cppreference.com/w/cpp/utility/initializer_list). Also see http://stackoverflow.com/q/495021/1782465 – Angew is no longer proud of SO Dec 03 '15 at 14:52
  • @Clix Why have you commented out `template` lines? And are you expecting `{1,2,3}` to call `Vector(size_type n, const T& val, const A&)` overload? – LogicStuff Dec 03 '15 at 14:54
  • @LogicStuff I forgot I still had them commented. Someone on another board suggested that templates were not allowed in anything expect header files. – Clix Dec 03 '15 at 14:59
  • 1
    Yeah, that's true. But the solution is to move the whole definitions (with `template` prefixes) to the header file. There's no other way. – LogicStuff Dec 03 '15 at 15:02
  • I did end up moving them. I still receive the same error as before. I believe the problem is what Angew suggested; although, I am not quite sure how to implement it. – Clix Dec 03 '15 at 15:07

1 Answers1

0

To my knowledge all template code has to be in headers, because the actual implementation is done by the compiler. The reason the initialization fails is that the compiler cannot find the definition for your functions.

Move all your code into vector.hpp.

Edit: Also, you need a constructor that takes initializer_list as an argument.

mrDudePerson
  • 327
  • 3
  • 12