问题
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
回答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
- the ful answer is already given by
g++
- 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
回答2:
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;
}
来源:https://stackoverflow.com/questions/54009236/dynamic-array-template-class-problem-with-ostream-operator-friend-function