Overloading a multiplication operator in a template class array - c++

I'm trying to code a template class for making arrays that has an overloaded multiplication operator. The actual insides of the template class appear fine, however I am having trouble with the overloading of the * operator. I originally had an overloaded operator for multiplying arrays in a regular class, here I am using that first overloaded operator as a base for the template.
This is whats in my header file :
operator * (const Array<T>& a) const; //* operator
And this is whats in my main file :
template <class T>
Array<T>::operator * (const Array<T>& a) const
{
if (num != a.num)
{
cout << "Error, arrays not equal!" << endl;
}
Array<int> tmp;
delete[] tmp.data;
tmp.data = new int[cap];
tmp.num = a.num;
tmp.cap = a.cap;
memcpy(tmp.data, a.data, sizeof(int)*num);
for(int i = 0 ; i < num ; i++)
{
tmp.data[i] = tmp.data[i] * a.data[i];
}
return tmp;
}
The error I am receiving is telling me that something is wrong with tmp. It states "error: cannot convert 'Array' to 'int' in return" in reference to "return tmp" at the end of the function.
I am trying to multiply two arrays (a and c) created in main()
Array <int> d = a * c;
There are error messages relating to Array d, but those also appear to be rooted in the error inside the overloaded operator function. How is tmp being turned into 'int' ?

You need to template Array<int> tmp; as Array<T> tmp;, so that then you memcpy(tmp.data, a.data, sizeof(int)*num); the size of memory area would realy be same. Also, as #Thecocatrice mentioned, add retutn type to you overloaded operator.
This will look like
template<typename T>
Array<T> Array<T>::operator *(const Array<T> &a) { ... };
And last. You dont actually need that memcpy. It doesnt make any sence, since you still have loop. Just write:
for(int i = 0 ; i < num ; i++)
{
tmp.data[i] = data[i] * a.data[i];
}

Your function does not have a return type. C++ will therefore default to int.
to solve this, make sure the function returns an Array<int> (the type of tmp).

Related

Automatic deduction of type of added array

I am quite new to c++ and I try to create an Array class in c++17 with the use of templates. In this class I overload the + operator, in such a way that it can add Arrays of multiple types. It does work so far and I am able to add arrays of different e.g. float and int type together. However, I am having some trouble with how to define the type of the new array, which is the result of the addition.
Let's say the arrays which I add are of type float and int. Then the new array should also be float. However, on forehand I dont know which array has the float type, the first one or the second one, so I can't create a new array with typename T or U.
Also, if due to coindicdence two float arrays add up together to only int values (e.g. 1.5 + 3.5 = 5(int) ), then the new array should be of type int.
Basically in summary, I try to define the type of the new array based on the type of the content after addition.
I came across some solutions that include decltype. However I can't manage to find a way how to include this for multiple values, since the array has more than one value. In my current code I create the new array based on the type T. However, if in a case T is of type int and U of type float, the result is not correct.
Any advice or tips are much appreciated.
Thanks in advance,
template <typename T>
class Array {
public:
T* content;
int length;
// Default Constructor
Array() : content(nullptr), length(0) {}
// Constructor when length is provided
Array(int length) : content(new T[length]), length(length) {}
// Constructor (using initializer list)
Array(std::initializer_list<T> list) : Array((int)list.size()) {
std::uninitialized_copy(list.begin(), list.end(), content);
}
// Obtain content at index i
float& operator[](int i) { return content[i]; }
// Adding arrays
template <typename U>
Array& operator+(Array<U>& other) {
Array<T>* new_array = new Array(other.length);
for (auto i = 0; i < other.length; i++)
new_array->content[i] = this->content[i] + other.content[i];
return *new_array;
}
};
With decltype, your operator + might look like:
template<typename U>
auto operator+(const Array<U>& rhs)
-> Array<std::decay_t<decltype((*this)[0] + rhs[0])>>
{
Array<std::decay_t<decltype((*this)[0] + rhs[0])>> res(rhs.length);
for (auto i = 0; i != rhs.length; i++) {
res[i] = (*this)[i] + rhs[i];
}
return res;
}
Demo

C: var was not declared in this scope

I am attempting to get my array wrapper class to compile, but I'm new to c++. I keep getting a series of relating to the last function:
Line 81
Invalid Use of template-name 'warray' without an arugment list
Line 81
ISO C++ forbids declaration of 'parameter' with not type
line 81
error expected ',' or '...' before < town
line 83
rhs was not declared in this scope
and finally, line 86
rhs was not declared in this scope
This function is so confusing, and I think I implemented it all correct.
IDK! Please help!
#ifndef WARRAY
#define WARRAY
#include <iostream>
#include <stdexcept>
template <typename T>
class warray {
private:
unsigned int theSize;
T* theData;
public:
//will default to a size of 10 - bump to 10 if below
warray(unsigned int size = 10){
if(size < 10){
size = 10;
}
theSize = size;
theData = new T[theSize];
}
//copy
warray(const warray &rhs):theSize(rhs.theSize){
theData = new T[theSize];
//use assignment*this = rhs;
*this = rhs;
}
//assignment
warray & operator=(const warray &rhs){
//only resize array if lhs < than rhs//this also remedies
if(theSize < rhs.theSize){
delete [] theData;
theData = new T[rhs.theSize];
}
theSize = rhs.theSize;
for(unsigned int i = 0; i < theSize; ++i){
(*this);
}
return *this;
}
//destrctor
~warray(){
delete [] theData;
}
//operator+ will concatenate two arrays should be const
warray operator+(const warray &rhs) const{
warray toRet(theSize + rhs.size);
for(unsigned int i = 0; i < theSize; ++i){
toRet[i] = (*this)[i];
}
for(unsigned int i = 0; i < theSize; ++i){
toRet[i+theSize] = rhs[i];
}
return warray();
}
//operator[unsigned T index]
//will index and allow access to requested element
// - two versions, const and non-const
T operator[](unsigned int index) const{
if(index >= theSize){
throw std::out_of_range ("in operator [] ");
}
return theData[theSize];
}
//size
unsigned int size() const{
return theSize;
}
};
std::ostream &operator<< (std::ostream &os, const warray&<T> rhs){
os << "[ ";
for(unsigned i = 0; i < rhs.size()-1; ++i){
os << rhs[i] << " , ";
}
os << rhs[rhs.size() - 1] << " ]";
return os;
}
#endif
This line:
T *theData [theSize];
attempts to declare an array of pointers of size theSize, but theSize is not a constant and is not known at compile time. You also don't want an array of pointers to T, you want a pointer to an array of T.
change it to
T *theData;
There are other problems with your code. e.g. your << operator needs to be a template and I have no idea what (*this) is trying to achieve.
Note: I am assuming you are doing this for learning purposes and can't simply use a vector.
Edit: The "Invalid Use of template-name 'warray' without an argument list" error is caused by the << operator not having template<typename T> in front of it. It needs this to make it a templated function.
You have marked this as C++.
I recommend you change from:
class warray {
private:
unsigned int theSize;
T* theData;
And perhaps try:
class warray {
private:
std::vector<T> theData;
What you call "theSize" is now available as theData.size().
To append values of T, use push_back().
If you desire, you can allocate a start size using theData.reserve(size), but not necessary.
Remove
delete [] theData;
because you no longer 'new'd it in the ctor.
The vector's dtor will be called automagically when your warray instance is dtor'd.
Can't define theData this way:
T *theData[theSize];
defining the size of a static array with a variable is non-standard. Some compilers allow it, some don't. Best practice is not to do it so you don't get tripped up. As it is, the Size has no defined value at that point so even if your compiler did that trick, there is a ton of ka-boom potential.
Fortunately, based on the rest of the code, you don't need to. You keep defining the size of the array yourself, so:
T *theData;
Should do you just fine.

Overload operator "+" in c++ depending from the parameters

Let us consider
we have created our own class Array. And now we want to overload "+" operator so that if:
Array a(5), b(3);
Array c = a + b;
it simply glues together a array and b array and we get a c array with length 5+3=8.
Now, I have realised this case writing in me Array.cpp file:
Array Array::operator+(const Array &Right)
{
Array temp_A(m_iCurInd+Right.m_iCurInd);
// here some stuff, not important to the quastion//
return temp_A;
}
And everything is fine.
But what if I want to :
Array a(5);
Array c = a + 2;
Array d = 2 + a;
So that d and c had lengths 6, with "2" in the beginning and in the end respectively?
I have tried
to realise the firs case, so in Array.h file it was such a line:
Array operator+(const int &value); //a+2
And in Array.cpp:
Array Array::operator+(const int &value)
{
Array temp_A(m_iCurInd+1);
// here some stuff, not important to the quastion//
return temp_A;
}
But it doesn't compiles, because it doesn't likes the types (particulary const int & I think). How shell I make it? Shell it be:
Array int::operator+(const Array &Right){
....
}
for the "2+a" case or something simmilar? Please advise me.
You might do this:
class Array
{
// ...
// Insert an Element
public:
void prepend(int);
void append(int);
// Append an Array
void append(const Array&);
// ...
};
inline Array operator + (const Array& a, const Array& b) {
Array result(a);
result.append(b);
return result;
}
inline Array operator + (const Array& a, int b) {
Array result(a);
result.append(b);
return result;
}
inline Array operator + (int a, const Array& b) {
Array result(b);
result.prepend(a);
return result;
}
You would define operator+ as a non-member function to support mixed-mode arithmetic.
Array operator+( const Array &arr1, const Array &arr2 )
and then provide appropriate single argument constructor for the types you want to support mixed-mode arithmetic.
To allow the syntax Array + int you have two options: Create a member function overload of operator+ where the parameter it takes is an integer, or create a free operator+ whose parameters are Array and int respectively. It looks like you've done it correctly for the member function overload, but to make sure, it should look like this:
class Array
{
// ...
Array operator+(int rhs)
{
// ...
}
};
Now Array + int should work. If it doesn't, you've done something wrong inside the function.
To allow the syntax int + Array you need a free operator function because the member function won't overload int for the left side:
Array operator+(int lhs, Array const& rhs)
{
return rhs + lhs;
}
rhs + lhs calls the member function overload Array::operator+().

Two square bracket overloading

I am writing a matrix class in c++ and trying to overload some operator like = and >> and << etc.
I was unable to overload operator [][] for matrix class.
if i have an object of class matrix like M1 then i can use this way for giving value to each element:
M1[1][2]=5;
OR
int X;
X=M1[4][5];
Just overload operator[] and make it return a pointer to the respective row or column of the matrix. Since pointers support subscripting by [], access by the 'double-square' notation [][] is possible then.
You can also overload operator() with two arguments.
There is no operator[][] in C++. You have to return a helper object and then overload operator[] for that too, to have this kind of access.
You could overload operator[]. So if you would like to use matrix that way, you should make matrix as array of vectors.
class Matrix
{
...
Vector & operator[]( int index );
...
};
and
class Vector
{
...
double & operator[]( int index );
...
};
Finally:
Matrix m;
...
double value = m[i][j];
...
there is no operator[][], you can implement operator[] to return a reference to the row/column object, in which you can implement the operator[] to return you the cell reference.
You can do something like the following to avoid all that hassle..
struct loc
{
int x;
int y;
};
then in your operator[] overload, accept a loc, something like
T& operator[](loc const& cLoc)
{
// now you have x/y you can return the object there.
}
To call, you can simply do something like:
matrix[loc(2,3)] = 5;
Actually, I did just that in my own matrix class a few years ago. In this case, I defined a matrix template class that contained the snippet, below.
I was then able to iterate and assign as follows:
for(size_t k=1; k<n; ++k) {
minor[p][k-1]=major[j][k];
}
I hope this helps.
// //////////////////////////////////////////////////////////////////////////////
// list is internal vector representation of n x m matrix
T* list;
// Proxy object used to provide the column operator
template < typename T >
class OperatorBracketHelper
{
Matrix < T > & parent ;
size_t firstIndex ;
public :
OperatorBracketHelper ( Matrix < T > & Parent , size_t FirstIndex ) :
parent ( Parent ), firstIndex ( FirstIndex ) {}
// method called for column operator
T & operator []( size_t SecondIndex )
{
// Call the parent GetElement method which will actually retrieve the element
return parent.GetElement ( firstIndex , SecondIndex );
}
};
// method called for row operator
OperatorBracketHelper < T > operator []( size_t FirstIndex )
{
// Return a proxy object that "knows" to which container it has to ask the element
// and which is the first index (specified in this call)
return OperatorBracketHelper < T >(* this , FirstIndex );
}
T & GetElement ( size_t FirstIndex , size_t SecondIndex )
{
return list[FirstIndex*cols+SecondIndex];
}
I am exactly working on a matrix class and I decided to first create an Array class which has a dynamic 2-D array. So, well just as you, I confronted this obstacle that how I can overload two square brackets. How I approached this case is very simple; I overloaded the square brackets operator twice as member functions. First, I overloaded [] so as to return a pointer pointing to the desired row, so to speak, and then the following member function (i.e. again operator [] overloaded) returns a lvalue of the same type as the array's elements.
However, note that the index you inter to invoke the former overloaded operator [] must be saved somewhere so that you may use it in the latter overloaded operator []. For this reason I simply added a new member of the type int to the class Array (which I've named it "test" in my code below).
class Array {
private:
double **ptr; int test;
... /* the rest of the members includes the number of rows and columns */
public:
Array(int=3,int=3); // Constructor
Array(Array &); // Copy Constructor
~Array(); // Destructor
void get_array();
void show_array();
double* operator[] (int);
double operator[] (short int);
...
};
...
double* Array::operator[] (int a) {
test = a;
double* p = ptr[test];
return p;
}
double Array::operator[] (short int b) {
return ((*this)[test][b]);
}
Therefor, as an example, in main I can simply write:
int main(){
Array example;
cout << example[1][2];
}
I hope this would help you.
You can't overload [][] as such, since there isn't such an
operator. You can overload [] to return something which also
has an [] defined on it (a proxy); in the simplest case,
something like a double* will work, but it's usually better,
although a bit more work, to use a full class. (Place to add
bounds checking, for example.)
Alternatively, you can overload (x,y). Depending on who you
ask, one format or the other is "better". (In fact, it's
strictly a question of style.)
Template matrix class
template <uint8_t rows, uint8_t cols, typename T>
class MatrixMxN {
public:
T* operator[](uint8_t f_row) {
return &m_val[f_row * cols];
}
protected:
T m_val[rows*cols];
};
and here is object of matrix with 3 row, 4 column and integer type.
MatrixMxN<3, 4, int32_t> M;
M[2][3] = 10;
std::cout << M[2][3] << std::endl;

C++ Operator overloading - 'recreating the Vector'

I am currently in a collage second level programing course... We are working on operator overloading... to do this we are to rebuild the vector class...
I was building the class and found that most of it is based on the [] operator. When I was trying to implement the + operator I run into a weird error that my professor has not seen before (apparently since the class switched IDE's from MinGW to VS express...) (I am using Visual Studio Express 2008 C++ edition...)
Vector.h
#include <string>
#include <iostream>
using namespace std;
#ifndef _VECTOR_H
#define _VECTOR_H
const int DEFAULT_VECTOR_SIZE = 5;
class Vector
{
private:
int * data;
int size;
int comp;
public:
inline Vector (int Comp = 5,int Size = 0)
: comp(Comp), size(Size) { if (comp > 0) { data = new int [comp]; }
else { data = new int [DEFAULT_VECTOR_SIZE];
comp = DEFAULT_VECTOR_SIZE; }
}
int size_ () const { return size; }
int comp_ () const { return comp; }
bool push_back (int);
bool push_front (int);
void expand ();
void expand (int);
void clear ();
const string at (int);
int& operator[ ](int);
int& operator[ ](int) const;
Vector& operator+ (Vector&);
Vector& operator- (const Vector&);
bool operator== (const Vector&);
bool operator!= (const Vector&);
~Vector() { delete [] data; }
};
ostream& operator<< (ostream&, const Vector&);
#endif
Vector.cpp
#include <iostream>
#include <string>
#include "Vector.h"
using namespace std;
const string Vector::at(int i) {
this[i];
}
void Vector::expand() {
expand(size);
}
void Vector::expand(int n ) {
int * newdata = new int [comp * 2];
if (*data != NULL) {
for (int i = 0; i <= (comp); i++) {
newdata[i] = data[i];
}
newdata -= comp;
comp += n;
data = newdata;
delete newdata;
}
else if ( *data == NULL || comp == 0) {
data = new int [DEFAULT_VECTOR_SIZE];
comp = DEFAULT_VECTOR_SIZE;
size = 0;
}
}
bool Vector::push_back(int n) {
if (comp = 0) { expand(); }
for (int k = 0; k != 2; k++) {
if ( size != comp ){
data[size] = n;
size++;
return true;
}
else {
expand();
}
}
return false;
}
void Vector::clear() {
delete [] data;
comp = 0;
size = 0;
}
int& Vector::operator[] (int place) { return (data[place]); }
int& Vector::operator[] (int place) const { return (data[place]); }
Vector& Vector::operator+ (Vector& n) {
int temp_int = 0;
if (size > n.size_() || size == n.size_()) { temp_int = size; }
else if (size < n.size_()) { temp_int = n.size_(); }
Vector newone(temp_int);
int temp_2_int = 0;
for ( int j = 0; j <= temp_int &&
j <= n.size_() &&
j <= size;
j++) {
temp_2_int = n[j] + data[j];
newone[j] = temp_2_int;
}
////////////////////////////////////////////////////////////
return newone;
////////////////////////////////////////////////////////////
}
ostream& operator<< (ostream& out, const Vector& n) {
for (int i = 0; i <= n.size_(); i++) {
////////////////////////////////////////////////////////////
out << n[i] << " ";
////////////////////////////////////////////////////////////
}
return out;
}
Errors:
out << n[i] << " "; error C2678:
binary '[' : no operator found which
takes a left-hand operand of type
'const Vector' (or there is no
acceptable conversion)
return newone;
error C2106: '=' : left
operand must be l-value
As stated above, I am a student going into Computer Science as my selected major I would appreciate tips, pointers, and better ways to do stuff :D
This:
int operator[ ](int);
is a non-const member function. It means that it cannot be called on a const Vector.
Usually, the subscript operator is implemented such that it returns a reference (if you return a value, like you are doing, you can't use it as an lvalue, e.g. you can't do newone[j] = temp_2_int; like you have in your code):
int& operator[](int);
In order to be able to call it on a const object, you should also provide a const version of the member function:
const int& operator[](int) const;
Since you ask for "tips, pointers, and better ways to do stuff:"
You cannot name your include guard _VECTOR_H. Names beginning with an underscore followed by a capital letter are reserved for the implementation. There are a lot of rules about underscores.
You should never use using namespace std in a header.
Your operator+ should take a const Vector& since it is not going to modify its argument.
Your at should return an int and should match the semantics of the C++ standard library containers (i.e., it should throw an exception if i is out of bounds. You need to use (*this)[i] to call your overloaded operator[].
You need to learn what the * operator does. In several places you've confused pointers and the objects to which they point.
Watch out for confusing = with == (e.g. in if (comp = 0)). The compiler will warn you about this. Don't ignore warnings.
Your logic will be much simpler if you guarantee that data is never NULL.
Can't fit this into a comment on Neil's answer, so I'm gonna have to go into more detail here.
Regarding your expand() function. It looks like this function's job is to expand the internal storage, which has comp elements, by n elements, while maintaining the size of the Vector. So let's walk through what you have.
void Vector::expand(int n) {
int * newdata = new int [comp * 2];
Okay, you just created a new array that is twice as big as the old one. Error: Why doesn't the new size have anything to do with n?
if (*data != NULL) {
Error: *data is the first int element in your array. It's not a pointer. Why is it being compared to NULL?
Concept Error: Even if you said if (data != NULL), which could be a test to see if there is an array at all, at what point in time is data ever set to NULL? new [] doesn't return NULL if it's out of memory; it throws an exception.
for (int i = 0; i <= (comp); i++) {
newdata[i] = data[i];
}
Warning: You're copying the whole array, but only the first size elements are valid. The loop could just run up to size and you'd be fine.
newdata -= comp;
Error: Bad pointer math. newdata is set to a pointer to who knows where (comp ints back from the start of newdata?!), and almost certainly a pointer that will corrupt memory if given to delete [].
comp += n;
This is fine, for what it is.
data = newdata;
delete newdata;
}
Error: You stored a pointer and then immediately deleted its memory, making it an invalid pointer.
else if ( *data == NULL || comp == 0) {
data = new int [DEFAULT_VECTOR_SIZE];
comp = DEFAULT_VECTOR_SIZE;
size = 0;
}
}
Error: This should be in your constructor, not here. Again, nothing ever sets data to NULL, and *data is an int, not a pointer.
What this function should do:
create a new array of comp + n elements
copy size elements from the old array to the new one
delete the old array
set data to point to the new array
Good luck.
Besides of what others already wrote about your operator[]():
Your operator+() takes the right-hand side per non-const reference - as if it would attempt to change it. However, with A+B everyone would expect B to remain unchanged.
Further, I would implement all binary operators treating their operands equally (i.e., not changing either of them) as non-member functions. As member functions the left-hand side (this) could be treated differently. (For example, it could be subjected to overwritten versions in derived classes.)
Then, IME it's always good to base operator+() on operator+=(). operator+=() does not treat its operands equally (it changes its left one), so it's best done as a member function. Once this is done, implementing operator+() on top of it is a piece of cake.
Finally, operator+() should never, never ever return a reference to an object. When you say A+B you expect this to return a new object, not to change some existing object and return a reference to that.
There are so many errors in your code that it is hard to know where to start. Here's one:
delete [] data;
*data = *newdata;
You delete a pointer and then immediately dereference it.
And:
const string Vector::at(int i) {
this[i];
}
This is (I think) a vector of ints. why is this returning a string? And applying the [] operator to this does not call your operator[] overload - it treats this as an array, which it isn't.
You need to provide two versions of your operator[]. For accessing:
T operator[](std::size_t idx)const;
For writing to the element:
T& operator[](std::size_t idx);
In both of the above, replace T with the type of the elements. The reason you have this problem is that only functions that are marked "const" may be invoked on an object declared to be "const". Marking all non-mutating functions as "const" is definitely something you should do and is called "const-correctness". Since returning a reference to an element (necessary for writing) allows the underlying object to be mutated, that version of the function cannot be made "const". Therefore, a read-only "const" overload is needed.
You may also be interested in reading:
Const Correctness from the C++ FAQ Lite
Const Correctness in C++
int Vector::operator[] (int place) { return (data[place]); }
This should be
int Vector::operator[] (int place) const { return (data[place]); }
so that you will be able to do the [] operation on const vectors. The const after the function declaration means that the class instance (this) is treated as const Vector, meaning you won't be able to modify normal attributes. Or in other words: A method that only has read access to attributes.