Search code examples
c++templatesvector

A function with a template in its argument throws an error when called with an int


I am trying to implement my own versioin of std::vector for practice. This is my first time working with templates so it's possible the solution is trivial but I have not been able to find anything on Google. The vector class I created has a template T which is supposed to be the variable type the vector is storing. In the push_back() function I am using "T value" as the argument. If I understand correctly that should make the function able to use whatever value the vector is initialized to store as the argument.

In main I am calling myvector.push_back(100) and getting the following error:

no instance of function template "myVector<T>::push_back [with T=int]" matches the argument listC/C++(304)
CustomContainer.cpp(112, 13): argument types are: (int)
CustomContainer.cpp(112, 13): object type is: myVector<int>

as well as:

"no matching function for call to 'myVector::push_back(int)'".

Here is my code:

template <typename T> 
class myVector
{
    private:
        int my_size = 0;
        int capacity = 1;
        T* arr;

    public:
        myVector() 
        {
            my_size = 0;
            capacity = 1;
            arr = new T[1];
        }

        myVector(unsigned int new_cap)
        {
            capacity = new_cap;
            arr = new T[new_cap];
        }

        myVector(unsigned int new_cap, T content)
        {
            capacity = new_cap;
            arr = new T[new_cap];
            std::fill_n(*arr, new_cap, content);
        }

        ~ myVector()
        {
            delete[] arr;
        }

        template <typename C>
        void push_back(T value)
        {
            if(my_size < capacity)
            {
                capacity = ceil((double)capacity * 1.5);
                C* temp = new C[capacity];
                std::copy(std::begin(arr), std::end(arr), std::begin(temp));
                delete arr;
                arr = temp;
                temp = NULL;
                my_size++;
            }
            arr[my_size++] = value;
        }

//more methods here, none of which are named "push_back"
};

int main()
{
    myVector<int> poggers;
    poggers.push_back(100);
}

I have tried creating the following function:

void push_back(int value)
{

}

and it made the error go away so it looks to me like a templates issue.

Any class design critique is also welcome.


Solution

  • The function is generally broken. You should not be using a distinct template parameter type C, when the class itself stores type T. Otherwise you'll get type mismatches when you allocate a new array of C.

    You were also using the wrong delete in that function, and std::begin / std::end don't work for raw pointers. Just use the pointers themselves, as they are compatible with iterators.

    It's also not a great idea to use floating point arithmetic for the expansion. You can use integers instead. Although it's a bit fiddly due to your initial size of 1, you can handle the size-1 edge case by adding 1 and avoiding any branching:

    capacity += (capacity + 1) / 2;
    

    Also made the argument a reference instead of copy, and removed a double size increase.

    The not-large-enough test was backwards. You need my_size == capacity. More "accident-proof" to use my_size >= capacity, I think, but watch out for future logic if you're pushing multiple values at once. You'll need to ensure that you select a large enough size.

    void push_back(const T& value)
    {
        if(my_size >= capacity)
        {
            capacity += (capacity + 1) / 2;
            T* temp = new T[capacity];
            std::copy(arr, arr + my_size, temp);
            delete[] arr;
            arr = temp;
        }
        arr[my_size++] = value;
    }
    

    Note that it's probably not a good idea to use int for the sizes. Containers use an unsigned type, and usually std::size_t. You may want to add safety by ensuring you do not overflow your capacity when enlarging the vector.

    Because you have a non-trivial class with raw memory management, you'll need copy constructor / copy-assignment. See What is the Rule of Three?

    Consider also supporting move-semantics. Not only for push_back (avoiding the copy of non-trivial types that you may want to store in the vector) but more generally for your move-construction / move-assignment.

    Instead of managing raw pointers, it would be cleaner to use std::unique_ptr.