Hello! I was creating a custom array class as an exercise in my exploring of class and function templates.

Here's my header file:

#include <iostream>
template <typename el>
class Array
{
    friend std::ostream &operator<<(std::ostream&,const Array&);
    friend std::istream &operator>>(std::istream&,Array&);

public:
    Array(const int& arraySize=0);       //set all members of array with size arraySize to NULL
    Array(const Array&);
    ~Array();           //destructor
    size_t getSize() const;   //return size

    const Array &operator=(const Array&);
    bool operator==(const Array&) const;
    bool operator!=(const Array&) const;

    el &operator[](int);         //returns modifiable lvalue
    el operator[](int) const;    //returns non-modifiable rvalue

private:
    size_t size;    //ptr-based array size
    el* ptr;
};

After I created the header file, I created the .cpp file, and I defined my class in it.
After I compiled this (please keep in mind that I did not import my header file into main, I did not get any error compiling).

However, when I imported the header file into main... I got errors linking the functions of the Array class that were called.

Here's my definition of the class in the .cpp file:

//  TemplatedArray class definition

#include <iostream> 
#include <iomanip>
#include <stdexcept>
#include "TemplatedArray.h"
using namespace std;

//default constructor for class Array
template <typename el>
Array<el>::Array(const int& arraySize):
size(arraySize > 0 ? arraySize : throw invalid_argument("Array size must be greater than 0")),ptr(new el[size])
{
    for (size_t i=0; i < size; ++i)
        ptr[i]=NULL;    //undefined value
}

template<typename el>
Array<el>::Array(const Array<el>& arrayToCopy):size(arrayToCopy.size),ptr(new el[size])
{
    for (size_t i=0; i < size; ++i)
        //copy each member of arrayToCopy into object
        ptr[i]=arrayToCopy.ptr[i];
}

template<typename el>
Array<el>::~Array<el>()
{
    delete [] ptr;
}

//return member of elements of array
template<typename el>
size_t Array<el>::getSize() const
{
    return size;
}

template<typename el>
const Array<el>& Array<el>::operator=(const Array<el> &right)
{
    if (right != this)  //avoid self-assignment
    {
        //for arrays of different sizes, deallocate original
        //left-side Array, then allocate new left-size Array
        if (size != right.size)
        {
            delete [] ptr;
            size=right.size;
            ptr=new el[size];
        }

        for (size_t i=0; i < size; ++i)
            ptr[i]=right.ptr[i];
    }
}


//determine if two Arrays are equal and return true, otherwise return false
template<typename el>
bool Array<el>::operator==(const Array<el>& right) const
{
    if (size != right.size)
        return false;

    for (size_t i=0; i < size; i++)
        if (ptr[i] != right.ptr[i])
            return false;   //Array contents not equal

    return true; //Array contents are equal
}

//determine if two Arrays are not equal, and return true, otherwise return false
template<typename el>
bool Array<el>::operator!=(const Array<el>& right) const
{
    return !(this == right);
}

//return modifiable lvalue
template<typename el>
el& Array<el>::operator[](int subscript)
{
   //check for out-of-range error
    if (subscript < 0 || subscript >=0)
        throw out_of_range("Subscript out of range");

    return ptr[subscript];
}

//overloaded subscript operator for const Arrays
//const reference return creates an rvalue
template<typename el>
el Array<el>::operator[](int subscript) const
{
    if (subscript < 0 || subscript >= size)
        throw out_of_range("Subscript out of range");

    return ptr[subscript];  //returns copy of the element
}

//overloaded input iterator for class Array
//inputs values for entire array
template<typename el>
istream &operator>>(istream& input,const Array<el>& a)
{
    //output private ptr-based array
    for (size_t i=0; i < a.size; i++)
        input >> a.ptr[i];

    return input;   //enable cin >> x >> y
}

//overloaded output operator for class Array
template<typename el>
ostream &operator<<(ostream &output,const Array<el>& a)
{
    //output private ptr-based array
    for (size_t i=0; i < a.size; i++)
    {
        output << setw(12) << a.ptr[i];

        if ((i+1)%4 == 0)   //four numbers per row of output
            output << endl;
    }

    if (a.size%4 != 0)      //end last line of output
        output << endl;

    return output;
}

What am I doing wrong? Am I making a mistake in declaring the typename el each time before I define the class functions and the inputstream and outputstream operators?

Depending on the compiler you are using you might have to have everything in the header file instead of splitting it up like a normal class. I would put everything into the header file and see if that fixes the error.

Also on line 15 in the cpp file you have ptr[i]=NULL; //undefined value. What happens if ptr is of type that can not be equal to NULL? This is why the STL uses allocators

Edited 2 Years Ago by NathanOliver

Thanks for your suggestion of putting the class definition in the header file.

Here's my updated header file:

//  Class definition for class Array
#ifndef __Array_class_using_class_templates__TemplatedArray__


#define __Array_class_using_class_templates__TemplatedArray__

#include <iostream>
#include <stdexcept>    //compiler does not see these imports
#include <iomanip>      //compiler does not see these imports

template <typename el>
class Array
{
    friend std::ostream &operator<<(std::ostream& output,const Array<el>& a)
    {
        //output private ptr-based array
        for (size_t i=0; i < a.size; i++)
        {
            output << setw(12) << a.ptr[i];

            if ((i+1)%4 == 0)   //four numbers per row of output
                output << std::endl;
        }

        if (a.size%4 != 0)      //end last line of output
            output << std::endl;

        return output;
    }

    friend std::istream &operator>>(std::istream& input,const Array<el>& a)
    {
        //output private ptr-based array
        for (size_t i=0; i < a.size; i++)
            input >> a.ptr[i];

        return input;   //enable cin >> x >> y
    }

public:
    Array(const int& arraySize=0):
    size(arraySize > 0 ? arraySize : throw invalid_argument("Array size must be greater than 0")),ptr(new el[size])
    {
        for (size_t i=0; i < size; ++i)
            ptr[i]=NULL;    //undefined value
    };       //set all members of array with size arraySize to NULL


    Array(const Array& arrayToCopy)
    {
        for (size_t i=0; i < size; ++i)
            //copy each member of arrayToCopy into object
            ptr[i]=arrayToCopy.ptr[i];
    }

    ~Array()
    {
        delete [] ptr;
    }

    size_t getSize() const
    {
        return size;
    }

    const Array<el> &operator=(const Array<el>& right)
    {
        if (right != this)  //avoid self-assignment
        {
            //for arrays of different sizes, deallocate original
            //left-side Array, then allocate new left-size Array
            if (size != right.size)
            {
                delete [] ptr;
                size=right.size;
                ptr=new el[size];
            }

            for (size_t i=0; i < size; ++i)
                ptr[i]=right.ptr[i];
        }

        return *this;
    }


    bool operator==(const Array& right) const
    {
        if (size != right.size)
            return false;

        for (size_t i=0; i < size; i++)
            if (ptr[i] != right.ptr[i])
                return false;   //Array contents not equal

        return true; //Array contents are equal
    }

    bool operator!=(const Array& right) const
    {
        return !(this == right);
    }

    el &operator[](int subscript)          //returns modifiable lvalue
    {
        //check for out-of-range error
        if (subscript < 0 || subscript >=0)
            throw out_of_range("Subscript out of range");

        return ptr[subscript];
    }

    el operator[](int subscript) const    //returns non-modifiable rvalue
    {
        if (subscript < 0 || subscript >= size)
            throw out_of_range("Subscript out of range");

            return ptr[subscript];  //returns copy of the element
    }


private:
    size_t size;    //ptr-based array size
    el* ptr;
};
#endif /* defined(__Array_class_using_class_templates__TemplatedArray__) */








//overloaded output operator for class Array
template<typename el>
std::ostream &operator<<(ostream &output,const Array<el>& a)
{
    //output private ptr-based array
    for (size_t i=0; i < a.size; i++)
    {
        output << setw(12) << a.ptr[i];

        if ((i+1)%4 == 0)   //four numbers per row of output
            output << std::endl;
    }

    if (a.size%4 != 0)      //end last line of output
        output << std::endl;

    return output;
}

However, I am getting compiler errors that are caused by the imports of the iostream, stdexcept, and iomanip libraries not occurring, even though they are being imported. What is going on with this?

I accounted for the usage of the std namespace.

What compiler are you using? It might be helpful if you posted the error message that you're getting (or at least the first couple of errors that you get) :)

I am using my xCode compiler on my mac.

There are many linker error messages that arose from this.

Here they are as following:

Undefined symbols for architecture x86_64:
  "Array<int>::Array(int const&)", referenced from:
      _main in main.o
  "Array<int>::~Array()", referenced from:
      _main in main.o
  "Array<int>::operator=(Array<int> const&)", referenced from:
      _main in main.o
  "Array<int>::operator[](int)", referenced from:
      _main in main.o
  "Array<int>::getSize() const", referenced from:
      _main in main.o
  "Array<int>::operator==(Array<int> const&) const", referenced from:
      _main in main.o
  "Array<int>::operator!=(Array<int> const&) const", referenced from:
      _main in main.o
  "operator<<(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, Array<int> const&)", referenced from:
      _main in main.o
  "operator>>(std::__1::basic_istream<char, std::__1::char_traits<char> >&, Array<int>&)", referenced from:
      _main in main.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I restored all my files to the state of my 10 linker error messages, and my code is as it was originally:

//  Class definition for class Array
#ifndef __Array_class_using_class_templates__TemplatedArray__
#define __Array_class_using_class_templates__TemplatedArray__

#include <iostream>
template <typename el>
class Array
{
    friend std::ostream &operator<<(std::ostream&,const Array&);
    friend std::istream &operator>>(std::istream&,Array&);

public:
    Array(const int& arraySize=0);       //set all members of array with size arraySize to NULL
    Array(const Array&);
    ~Array();           //destructor
    size_t getSize() const;   //return size

    const Array &operator=(const Array&);
    bool operator==(const Array&) const;
    bool operator!=(const Array&) const;

    el &operator[](int);         //returns modifiable lvalue
    el operator[](int) const;    //returns non-modifiable rvalue

private:
    size_t size;    //ptr-based array size
    el* ptr;
};
#endif /* defined(__Array_class_using_class_templates__TemplatedArray__) */

TemplatedArray.cpp:

//  TemplatedArray class definition

#include <iostream> 
#include <iomanip>
#include <stdexcept>
#include "TemplatedArray.h"
using namespace std;

//default constructor for class Array
template <typename el>
Array<el>::Array(const int& arraySize):
size(arraySize > 0 ? arraySize : throw invalid_argument("Array size must be greater than 0")),ptr(new el[size])
{
    for (size_t i=0; i < size; ++i)
        ptr[i]=NULL;    //undefined value
}

template<typename el>
Array<el>::Array(const Array<el>& arrayToCopy):size(arrayToCopy.size),ptr(new el[size])
{
    for (size_t i=0; i < size; ++i)
        //copy each member of arrayToCopy into object
        ptr[i]=arrayToCopy.ptr[i];
}

template<typename el>
Array<el>::~Array<el>()
{
    delete [] ptr;
}

//return member of elements of array
template<typename el>
size_t Array<el>::getSize() const
{
    return size;
}

template<typename el>
const Array<el>& Array<el>::operator=(const Array<el> &right)
{
    if (right != this)  //avoid self-assignment
    {
        //for arrays of different sizes, deallocate original
        //left-side Array, then allocate new left-size Array
        if (size != right.size)
        {
            delete [] ptr;
            size=right.size;
            ptr=new el[size];
        }

        for (size_t i=0; i < size; ++i)
            ptr[i]=right.ptr[i];
    }
}


//determine if two Arrays are equal and return true, otherwise return false
template<typename el>
bool Array<el>::operator==(const Array<el>& right) const
{
    if (size != right.size)
        return false;

    for (size_t i=0; i < size; i++)
        if (ptr[i] != right.ptr[i])
            return false;   //Array contents not equal

    return true; //Array contents are equal
}

//determine if two Arrays are not equal, and return true, otherwise return false
template<typename el>
bool Array<el>::operator!=(const Array<el>& right) const
{
    return !(this == right);
}

//return modifiable lvalue
template<typename el>
el& Array<el>::operator[](int subscript)
{
   //check for out-of-range error
    if (subscript < 0 || subscript >=0)
        throw out_of_range("Subscript out of range");

    return ptr[subscript];
}

//overloaded subscript operator for const Arrays
//const reference return creates an rvalue
template<typename el>
el Array<el>::operator[](int subscript) const
{
    if (subscript < 0 || subscript >= size)
        throw out_of_range("Subscript out of range");

    return ptr[subscript];  //returns copy of the element
}

//overloaded input iterator for class Array
//inputs values for entire array
template<typename el>
istream &operator>>(istream& input,const Array<el>& a)
{
    //output private ptr-based array
    for (size_t i=0; i < a.size; i++)
        input >> a.ptr[i];

    return input;   //enable cin >> x >> y
}

//overloaded output operator for class Array
template<typename el>
ostream &operator<<(ostream &output,const Array<el>& a)
{
    //output private ptr-based array
    for (size_t i=0; i < a.size; i++)
    {
        output << setw(12) << a.ptr[i];

        if ((i+1)%4 == 0)   //four numbers per row of output
            output << endl;
    }

    if (a.size%4 != 0)      //end last line of output
        output << endl;

    return output;
}

and my main file:

//  Array template class test program

#include <iostream>
#include <stdexcept>
#include "TemplatedArray.h"
using namespace std;

int main()
{
    Array<int> integers1(7);        //create array of integers with size 7
    Array<int> integers2(10);       //create array of integers with size 10


    //print integers1 size and contents
    cout << "Size of Array integers1 is: " << integers1.getSize() << "\nArray after initialization:\n" << integers1;

    //print integers2 size and contents
    cout << "\nSize of Array integers2 is: " << integers2.getSize() << "\nArray after initialization:\n" << integers2;

    //input and print integers1 and integers2
    cout << "\nEnter 17 integers: " << endl;
    cin >> integers1 >> integers2;

    cout << "\nAfter input, the Arrays contains:\n" << "integers1:\n" << integers1 << "integers2:\n" << integers2;

    //use the overloaded inequality operator
    cout << "\nEvaluating integers1 != integers2" << endl;
    if (integers1 != integers2)
        cout << "integers1 and integers2 are not equal" << endl;
    else
        cout << "integers1 and integers2 are equal" << endl;

    //use overloaded assignment operator
    cout << "\nAssigning integers2 to integers1:" << endl;
    integers1=integers2;    //note target array is smaller

    cout << "integers1:\n" << integers1 << "integers2:\n" << integers2;

    //use overloaded equality operator
    cout << "\nEvaluating: integers1 == integers2" << endl;
    if (integers1 == integers2)
        cout << "integers1 and integers2 are equal" << endl;
    else
        cout << "integers1 and integers2 are not equal" << endl;

    //use overloaded subscript operator to create rvalue
    cout << "\nintegers1[5] is: " << integers1[5];

    //use overloaded subscript operator to create lvalue
    cout << "\n\nAssigning 1000 to integers1[5]" << endl;
    integers1[5]=1000;
    cout << "integers1:\n" << integers1;

    //attempt to use out-of-range subscript
    try
    {
        cout << "\nAttempt to assign 1000 to integers1[15]" << endl;
        integers1[15]=1000; //ERROR: subscript out of range
    }
    catch (out_of_range &ex)
    {
        cout << "An exception occurred: " << ex.what() << endl;
    }
}

Can you check to see if these run on your machine?

I tried the actual code, and there are some "real" errors like in the != operator, where you have !(this == right) instead of !(*this == right), because this is a pointer, so it needs to be dereferenced to get a reference that can be compared with right.

In the assignment operator, you have if(right != this) which should be if(&right != this) to compare the address of both objects, to prevent self-assignment. Also, that function is missing the return statement return *this;.

There is a difference in the signature of the input operator >>, because the friend declaration has the array reference as non-const (as it should be in this case), and the definition of that operator has a const-reference.

The other obvious problem is the undefined behavior that NathanOliver pointed out, which you should so as this:

ptr[i] = el();    //default-construted value

which means that any class object will just be default initialized, while primitive values (like int, double, etc.) will be zero-initialized.

Another interesting little problem is with your friend declaration for the << or >> operators. This is a bit of a crazy mind bender, but essentially, when you declare a friend function like this, within a class template, you end up declaring, as a friend, a regular function (non-template) as a friend to a class template, but that regular function will only get created when you use the class template for a particular template argument(s). Long story short, there is no way the provide a definition (implementation) for that friend function outside of the class template declaration. So, you either have to do that, or see one of the alternatives explained here.

About the code organization, when you create a template (class or function) you cannot put the code inside a separate cpp file, because a template is not really "complete" code, because it is missing a crucial ingredient, i.e., the template argument(s), in this case "el". The compiler cannot "compile" template code until it has all the arguments, and the arguments are only provided when you use the class template, meaning that you need to put all class / function template code in the header file (there are some advanced tricks regarding this, but it's probably too much for you to chew right now).

Another issue you should work on is your coding habits. You should not have produced such a large chunk of code without even trying to compile it once. These kinds of issues will be a lot easier to solve if you write a little, compile, test, and write some more, and repeat. Keep the cycle shorts and the bugs easy to find.

Other than that, I think your good, the code seems to run fine, as far as I can tell.

You might want to check out my tutorial where I give some guidelines on writing such a class (custom array/vector).

Thanks for taking the time to help me out.
I put all the contents into one file, and I commented out the declaration for the friend functions (for input and output). That I want to do later (I have a question regarding this to ask you first).

Here's my header file, which imports the iostream, stdexcept, and iomanip libraries.

If you look at my constructor, and my two subscript operators, you will see that I have a comment stating //use of undeclared identifier. This shows the error I got on recompiling.

Apparently, my compiler does not see the importing of my <stdexcept> file, which declares these exceptions.
Why is the compiler not seeing the imports? I imported the files in a valid way.

I am also a bit confused with understanding the meaning of the text of the website to which you provided me the link.

I understand the reason why we have the class template definition and implementation in the header file, but could you suggest me a way to have the input and output operators working as friends of the class [in actual code with explanation], so I would understand? I commented out the definition and the implementation (which I copied and pasted from my .cpp to my .h file and commented out).

//  Class definition for class Array
#ifndef __Array_class_using_class_templates__TemplatedArray__
#define __Array_class_using_class_templates__TemplatedArray__

#include <iostream>
#include <iomanip>
#include <stdexcept>
template <typename el>
class Array
{
    /*      WILL DO INPUT AND OUTPUT OPERATORS LATER
    friend std::ostream &operator<<(std::ostream&,const Array<el>&);
    friend std::istream &operator>>(std::istream&,const Array<el>&);
     */

public:
    Array(const int& arraySize=0): size(arraySize > 0 ? arraySize : throw invalid_argument("Array size must be greater than 0")),ptr(new el[size]) //use of undeclared identifier 'invalid_argument' error- apparently does not see the import of the stdexcept library
    {
        //set all members of array with size arraySize to default value
        for (size_t i=0; i < size; ++i)
            ptr[i]=el();    //default-constructed value
    }


    Array(const Array<el>& arrayToCopy):size(arrayToCopy.size),ptr(new el[size])
    {
        for (size_t i=0; i < size; ++i)
            //copy each member of arrayToCopy into object
            ptr[i]=arrayToCopy.ptr[i];
    }


    ~Array()           //destructor
    {
        delete [] ptr;
    }


    size_t getSize() const   //return size
    {
    return size;
    }

    const Array<el> &operator=(const Array<el> &right)
    {
        if (&right != this)  //avoid self-assignment- check for memory slot
        {
            //for arrays of different sizes, deallocate original
            //left-side Array, then allocate new left-size Array
            if (size != right.size)
            {
                delete [] ptr;
                size=right.size;
                ptr=new el[size];
            }

            for (size_t i=0; i < size; ++i)
                ptr[i]=right.ptr[i];
        }

        return *this;
    }

    bool operator==(const Array<el> &right) const
    {
        if (size != right.size)
            return false;

        for (size_t i=0; i < size; i++)
            if (ptr[i] != right.ptr[i])
                return false;   //Array contents not equal

        return true; //Array contents are equal
    }

    bool operator!=(const Array<el> &right) const
    {
        return !(*this == right);
    }

    el &operator[](int subscript)         //returns modifiable lvalue
    {
        //check for out-of-range error
        if (subscript < 0 || subscript >=0)
            throw out_of_range("Subscript out of range"); //use of undeclared identifier 'out_of_range' error- apparently does not see the import of the stdexcept library

        return ptr[subscript];
    }

    el operator[](int subscript) const    //returns non-modifiable rvalue
    {
        if (subscript < 0 || subscript >= size)
            throw out_of_range("Subscript out of range"); //use of undeclared identifier 'out_of_range' error- apparently does not see the import of the stdexcept library

        return ptr[subscript];  //returns copy of the element
    }

private:
    size_t size;    //ptr-based array size
    el* ptr;
};
#endif /* defined(__Array_class_using_class_templates__TemplatedArray__) */



/*
        input and output operators- commented out due to putting class template definition in ONE file

 //overloaded input iterator for class Array
 //inputs values for entire array
 template<typename el>
 istream &operator>>(istream& input,const Array<el>& a)
 {
 //output private ptr-based array
 for (size_t i=0; i < a.size; i++)
 input >> a.ptr[i];

 return input;   //enable cin >> x >> y
 }

 //overloaded output operator for class Array
 template<typename el>
 ostream &operator<<(ostream &output,const Array<el>& a)
 {
 //output private ptr-based array
 for (size_t i=0; i < a.size; i++)
 {
 output << setw(12) << a.ptr[i];

 if ((i+1)%4 == 0)   //four numbers per row of output
 output << endl;
 }

 if (a.size%4 != 0)      //end last line of output
 output << endl;

 return output;
 }

 */

Since you do not have using namespace std; in your code you need to prepend std:: to everything you are using that is part of the STL. So throw invalid_argument("Array size must be greater than 0") becomes throw std::invalid_argument("Array size must be greater than 0"). I noticed you had it in your .cpp file.

Edited 2 Years Ago by NathanOliver

Yes, NathanOliver, thanks for noticing that. I fixed that, and now I need to figure out how to do the output and input operator overloading.

How can I make the input and output operators only friends of a particular template, so that it will be "friends" with only an Array of a certain template type el?

mike_2000_17's links explain my problem, but I do not understand how to implement a solution to this problem.

For your case I think it would be like the following like the following

template <typename el>
class Array
{
public:
    friend std::ostream & operator<< <el>(std::ostream& ouput,const Array& a)
    friend std::istream & operator>> <el>(std::istream& input,Array & list)
    //...
};

template <typename T>
friend std::ostream &operator<<(std::ostream& ouput,const Array<T>& a)
{
    //output private ptr-based array
    for (size_t i=0; i < a.size; i++)
    {
        output << setw(12) << a.ptr[i];

        if ((i+1)%4 == 0)   //four numbers per row of output
            output << endl;
    }

    output << endl;

    return output;
}

template <typename T>
friend std::istream &operator>>(std::istream& input,Array<T>& a)
{
    //output private ptr-based array
    for (size_t i=0; i < a.size; i++)
        input >> a.ptr[i];

    return input;   //enable cin >> x >> y
}

To create the input output operators as friends of your Array class template for a specific "el" type, you need to do the following set of little tricks:

// Make a forward-declaration of Array:
template <typename el>
class Array;

// Declare the operators:
template <typename el>
std::ostream &operator<<(std::ostream&, const Array<el>&);
template <typename el>
std::istream &operator>>(std::istream&, Array<el>&);


template <typename el>
class Array
{
    // Declare the specializations as friends:

    friend std::ostream &operator<< <el>(std::ostream&, const Array<el>&);
    friend std::istream &operator>> <el>(std::istream&, Array<el>&);


    // ... rest of code for Array...

}

// And then, put the definitions of the operators:

//overloaded input iterator for class Array
//inputs values for entire array
template<typename el>
istream &operator>>(istream& input, Array<el>& a)
{
  //output private ptr-based array
  for (size_t i=0; i < a.size; i++)
    input >> a.ptr[i];
  return input;   //enable cin >> x >> y
}

//overloaded output operator for class Array
template<typename el>
ostream &operator<<(ostream &output, const Array<el>& a)
{
  //output private ptr-based array
  for (size_t i=0; i < a.size; i++)
  {
    output << setw(12) << a.ptr[i];
    if ((i+1)%4 == 0)   //four numbers per row of output
      output << endl;
  }
  if (a.size%4 != 0) //end last line of output
    output << endl;
  return output;
}

And just a final note on your terminology:

I commented out the definition and the implementation

In C/C++ terminology, the words "definition" and "implementation" are essentially synonyms. The word "definition" is just the way that the standard documents refer to "where the actual code is", i.e., the implementation. So, in C++, we talk about declarations and definitions. For example, a function declaration (just the prototype of the function) is a declaration, and the implementation of that function (where the code is) is the definition. That might help you understand articles and other explanations.

Thanks for the help. I got the code to work right.

One other question:

You have this line in the class definition:

friend std::ostream &operator<< <el>(std::ostream&,const Array<el>&);

Why couldn't I do this instead (having that same line there with the <el> being removed)?:

friend std::ostream &operator<< (std::ostream&,const Array<el>&);

Why couldn't I do this instead (having that same line there with the <el> being removed)?:

Because the compiler will interpret this declaration as a regular function that takes a particular Array<el>, not as a particular specialization of the function template that can take any Array<el>. As I said earlier, this is one of those mind-bending things, but you'll understand it eventually.

This question has already been answered. Start a new discussion instead.