-1

Hi there fellow computer scientists, I'm having a lot of issues with my code, everything works except for the friend ostream& operator function. I keep getting a compiler error when sending my class object to cout. I'm thinking a made an error while declaring the friend function or maybe within its declaration.Here is the code:

By the way I know its traditional to use T for templates but I used my professors name, it sounds weird but using unconventional names in my code helps me remember programming concepts like templates and etc

#include <iostream>
#include <cstdlib>

using namespace std;

template <class Chris>
class DynamicArray{


private:
    Chris *myArray;
     int capacity;
     int num_items;


public:
  DynamicArray();
  DynamicArray(int initialCapacity);
  void reSize(int newCapacity);
  void addElement(const Chris element);
  Chris& operator[](int index)const;
  friend std::ostream& operator << (std::ostream& outputStream, const 
  DynamicArray<Chris>& obj);
  virtual ~DynamicArray();


};


int main(){



DynamicArray<int> Array(20);

Array.addElement(20);
Array.addElement(12);
Array.addElement(13);
Array.addElement(45);
Array.addElement(78);



cout<<Array<<endl;












return 0;
}



template<class Chris>
ostream& operator<< (ostream& outputStream, const DynamicArray<Chris>& 
obj)
{

for(int index=0; index<obj.num_items; index++){

    if(index<(obj.num_items-1)){
    outputStream<<obj.myArray[index]<<",";
    }

    else{

        outputStream<<obj.myArray[index];
    }
   }


   return outputStream;
  }

 template<class Chris>
 DynamicArray<Chris>::DynamicArray():capacity(1),num_items(0)
 {

     myArray=new Chris[capacity];
 }

template <class Chris>
DynamicArray<Chris>::DynamicArray(int initialCapacity):num_items(0)
{

       if(initialCapacity>0){

    capacity=initialCapacity;
    myArray=new Chris[capacity];
}

    else{

       cout<<"ERROR, capacity cannot be negative or 0 " <<endl;
       exit(0);

      }

   }

 template <class Chris>
void DynamicArray<Chris>::reSize(int newCapacity)
 {

    if(newCapacity<=capacity){


        cout<<"ERROR, the new capacity must be greater than the current 
      capacity"<<endl;
        exit(1);
    }

    Chris *biggerArray = new Chris[newCapacity];

    for(int index=0; index<num_items; index++){

        biggerArray[index]=myArray[index];



    }

    delete [] myArray;
    capacity=newCapacity;
    myArray= new Chris[capacity];

       for(int index=0; index<num_items; index++){

        myArray[index]= biggerArray[index];



    }

    delete [] biggerArray;


   }

template <class Chris>
Chris& DynamicArray<Chris>::operator [](int index)const
{


if(index>=num_items){


    cout<<"ERROR,ARRAYINDEX OUT OF BOUNDS " <<endl;
    exit(0);
}

return myArray[index];



}





 template<class Chris>
 void DynamicArray<Chris>::addElement(const Chris element){

    if(num_items==capacity){


        reSize(capacity*2);
    }


    myArray[num_items]=element;
    num_items++;



}



template<class Chris>
DynamicArray<Chris>::~DynamicArray()
{


  delete [] myArray;
}

The compiler error is :undefined reference ,also it states that my friend ostream& function was not declared as a template. I have to idea how to fix this

theredfox24
  • 87
  • 1
  • 2
  • 11
  • There are quite a few functions here that you haven't defined. Is that just because of how you've formed the question? – Lightness Races in Orbit Jan 02 '19 at 15:57
  • I didn't include the other definitions because they work fine, but if it bothers I shall add them now – theredfox24 Jan 02 '19 at 15:58
  • missing `std::` in the definition of `operator<<(std::ostream&, const DynamicArray&)` – Walter Jan 02 '19 at 16:01
  • Well you're supposed to present your [MCVE] otherwise how are we to know which errors are hidden away in code you didn't show us? – Lightness Races in Orbit Jan 02 '19 at 16:01
  • @Walter That wouldn't cause an undefined reference and there is a using namespace std anyway – Lightness Races in Orbit Jan 02 '19 at 16:02
  • The friend declaration does not match the template output stream. It should be a template friend. – Eljay Jan 02 '19 at 16:09
  • are you stating I should write it like this (adding the <>: friend std::ostream& operator << <>(std::ostream& outputStream, const DynamicArray& obj); – theredfox24 Jan 02 '19 at 16:13
  • Try to move the definition of your std::ostream& operator << <>(std::ostream& outputStream, const DynamicArray& obj); – alangab Jan 02 '19 at 16:25
  • No, I'm saying this... `template ostream& operator<< (ostream& outputStream, const DynamicArray& obj)` does not match this... `friend std::ostream& operator << (std::ostream& outputStream, const DynamicArray& obj);`. The first one is a template function, the second one is not a template function it is only a function. So they don't match. Turn the friend declaration into a template friend declaration. – Eljay Jan 02 '19 at 16:31
  • ok, thank you Eljay, I have corrected the code – theredfox24 Jan 02 '19 at 16:33

2 Answers2

1

To fix the issue above friend function must be declared at the top of the program before the class definition. The class that contains the friend functions should also be declared.I have edited my answer and also included the whole program.

#include <iostream>
#include <cstdlib>

using std::iostream;
using std::cout;
using std::endl;


template<typename Chris>
class DynamicArray; //must add this

template<typename Chris>
std::ostream& operator <<(std::ostream& outputStream, const 
DynamicArray<Chris>& 
    obj);// must add this as well



template <typename Chris>
 class DynamicArray{


    private:
    Chris *myArray;
    int capacity;
     int num_items;
     friend std::ostream& operator << <>(std::ostream& outputStream, const 
     DynamicArray& 
      obj);


    public:
    DynamicArray();
    DynamicArray(int initialCapacity);
    void reSize(int newCapacity);
    void addElement(const Chris element);
    Chris& operator[](int index)const;
    virtual ~DynamicArray();





















  };



  int main(){



   DynamicArray<int> Array(20);

   Array.addElement(20);
   Array.addElement(12);
   Array.addElement(13);
   Array.addElement(45);
   Array.addElement(78);



   cout<<Array<<endl;

    return 0;
   }

 template<typename Chris>
 DynamicArray<Chris>::DynamicArray():capacity(1),num_items(0)
   {

       myArray=new Chris[capacity];
    }

template <typename Chris>
DynamicArray<Chris>::DynamicArray(int initialCapacity):num_items(0)
    {

          if(initialCapacity>0){

           capacity=initialCapacity;
            myArray=new Chris[capacity];
      }

      else{

             cout<<"ERROR, capacity cannot be negative or 0 " <<endl;
              exit(0);

             }

            }

 template <typename Chris>
 void DynamicArray<Chris>::reSize(int newCapacity)
         {

                 if(newCapacity<=capacity){


                  cout<<"ERROR, the new capacity must be greater than the 
                 current capacity"<<endl;
                  exit(1);
                 }

                Chris *biggerArray = new Chris[newCapacity];

               for(int index=0; index<num_items; index++){

               biggerArray[index]=myArray[index];



              }

              delete [] myArray;
              capacity=newCapacity;
              myArray= new Chris[capacity];

               for(int index=0; index<num_items; index++){

               myArray[index]= biggerArray[index];



              }

              delete [] biggerArray;


             }

 template <typename Chris>
 Chris& DynamicArray<Chris>::operator [](int index)const
 {


    if(index>=num_items){


      cout<<"ERROR,ARRAYINDEX OUT OF BOUNDS " <<endl;
       exit(0);
   }

     return myArray[index];



   }



template<typename Chris>
void DynamicArray<Chris>::addElement(const Chris element){

    if(num_items==capacity){


        reSize(capacity*2);
    }


    myArray[num_items]=element;
    num_items++;



}

template<typename Chris>
std::ostream& operator<< (std::ostream& outputStream, const 
DynamicArray<Chris>&
obj)
{

   for(int index=0; index<obj.num_items; index++){

   if(index<(obj.num_items-1)){
    outputStream<<obj.myArray[index]<<",";
 }

     else{

         outputStream<<obj.myArray[index];
    }
   }


      return outputStream;
  }

template<typename Chris>
DynamicArray<Chris>::~DynamicArray()
{


   delete [] myArray;
}
theredfox24
  • 87
  • 1
  • 2
  • 11
1

Although OP seems to have solved her/his problem on its own, I became curious a bit.

OP's problem seems to be to declare a free-standing friend operator<< in a class template. OP's sample code is a bit hard to read, thus I made my own MCVE:

#include <iostream>
#include <exception>
#include <algorithm>

// template class for dynamic array
template <typename VALUE>
class VectorT {
    
  private:
    VALUE *_values;
    size_t _capacity;
    size_t _size;
    
  public:
    VectorT(): _values(nullptr), _capacity(0), _size(0) { }
    ~VectorT() { delete[] _values; }
    VectorT(const VectorT &vec); /// @todo
    VectorT& operator=(const VectorT &vec); /// @todo
    
    size_t capacity() const { return _capacity; }
    size_t size() const { return _size; }
    VALUE& operator[](size_t i) { return _values[i]; }
    const VALUE& operator[](size_t i) const { return _values[i]; }
    
    void push_back(const VALUE &value)
    {
      if (_size == _capacity) { // realloc necessary
        const size_t capacity = std::max(2 * _capacity, (size_t)1);
        VALUE *const values = new VALUE[capacity];
        if (!values) throw std::bad_array_new_length();
        std::move(_values, _values + _size, values);
        delete[] _values;
        _values = values; _capacity = capacity;
      }
      _values[_size++] = value;
    }

    friend std::ostream& operator<<(std::ostream&, const VectorT<VALUE>&);
    
};

// output stream operator for VectorT
template <typename VALUE>
std::ostream& operator<<(std::ostream &out, const VectorT<VALUE> &vec)
{
  const char *sep = "";
  for (size_t i = 0; i < vec._size; ++i) {
    out << sep << vec[i];
    sep = ", ";
  }
  return out;
}

// test
int main()
{
  VectorT<int> vec;
  // populate vec
  vec.push_back(20);
  vec.push_back(12);
  vec.push_back(13);
  vec.push_back(45);
  vec.push_back(78);
  // test output operator
  std::cout << vec << '\n';
  // done
  return 0;
}

Note: I changed the concept and names a bit as OP's DynamicArray does actually provide something similar like std::vector. I found it reasonable to resemble it a bit closer.

I tried to compile this with

g++ --version ; g++ -std=c++11 -O2 -Wall -pedantic main.cpp && ./a.out

and got the following output:

g++ (GCC) 8.1.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

main.cpp:38:73: warning: friend declaration 'std::ostream& operator<<(std::ostream&, const VectorT<VALUE>&)' declares a non-template function [-Wnon-template-friend]
     friend std::ostream& operator<<(std::ostream&, const VectorT<VALUE>&);
                                                                         ^
main.cpp:38:73: note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here) 
/tmp/ccvsl6kw.o: In function `main':
main.cpp:(.text.startup+0x9e): undefined reference to `operator<<(std::ostream&, VectorT<int> const&)'
collect2: error: ld returned 1 exit status

It's funny enough that

  1. the ful answer is already given by g++
  2. and it consists actually of two activities:

make sure the function template has already been declared

and

add <> after the function name here

Concerning the first part, I remembered a similar issue I once found in my answer to SO: Why would a struct need a friend function?.

The second part (add <> after the function name here) is something which raised my attention as I've never seen (nor used) it that way before. So, I'd like to elaborate a bit.

After having inserted the following forward declarations:

// forward declaration of VectorT
template <typename VALUE>
class VectorT;

// prototyping of output stream operator
template <typename VALUE>
std::ostream& operator<<(std::ostream&, const VectorT<VALUE>&);

I tried to compile again and got again:

main.cpp:46:73: warning: friend declaration 'std::ostream& operator<<(std::ostream&, const VectorT<VALUE>&)' declares a non-template function [-Wnon-template-friend]
     friend std::ostream& operator<<(std::ostream&, const VectorT<VALUE>&);
                                                                         ^
main.cpp:46:73: note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here) 
/tmp/ccXLnkbV.o: In function `main':
main.cpp:(.text.startup+0x9e): undefined reference to `operator<<(std::ostream&, VectorT<int> const&)'
collect2: error: ld returned 1 exit status

exactly as before. Oops!

My first reflex was to change the friend operator to a template friend operator:

template <typename VALUE_>
friend std::ostream& operator<<(std::ostream&, const VectorT<VALUE_>&);

and this solved the issue: Live Demo on coliru.


However, this solution has a little flaw which might or might not be annoying: Any operator instance is friend to any VectorT template instance. Actually, this should be constrained to only one operator instance – the one with the same VectorT template instance in signature. This is what g++ actually suggested:

friend std::ostream& operator<< <>(std::ostream&, const VectorT<VALUE>&);

Live Demo on coliru

I wonder why this isn't mentioned in theredfox24's answer – IMHO, this is the actually exciting part of OP's fix.


Finally, I'd like to mention that (in this case) the “whole friend magic” is completely unnecessary. This is what raised my attention first – in the dozens of written output operators (for class templates), I never had any friend issues. This can be easily prevented if the output operator uses public const members of the class exclusively (and I can hardly imagine why they shouldn't be available):

// output stream operator for VectorT
template <typename VALUE>
std::ostream& operator<<(std::ostream &out, const VectorT<VALUE> &vec)
{
  const char *sep = "";
  for (size_t i = 0; i < vec.size(); ++i) {
    out << sep << vec[i];
    sep = ", ";
  }
  return out;
}

(Forward declarations and friend operator removed because not needed anymore.)

Live Demo on coliru

Community
  • 1
  • 1
Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56