I have a problem figuring out to use the [] operator to assign a value, i have 2 different errors,
"binary '=': no operator found which takes a right-hand operand of type 'double' (or there is no acceptable conversion)"
no operator "=" matches these operands.
Am I missing something here?
Header
Class CMyVector{
public:
double operator[](const int index) const;
double & operator[](const int index);
CMyVector operator+(const CMyVector mv1);
}
cpp
double CMyVector::operator[](const int index) const
{
return arr[index];
}
double & CMyVector::operator[](const int index)
{
return arr[index];
}
CMyVector CMyVector::operator+(const CMyVector mv1)
{
CMyVector *retval = new CMyVector();
retval[0] = arr[i] + mv1[i];
return *this;
}
CMyVector::operator+ seems weird.
1.retval is a pointer, (i.e. CMyVector *), then retval[i] will be CMyVector, which doesn't make sense for retval[i] = arr[i] + mv1[i];. You might mean (*retval)[i] = arr[i] + mv1[i];. BTW: Here's a memory leak because you didn't delete the pointer.
2.You new a pointer retval, and then set value on it, at last return *this? It doesn't make sense. You might mean:
CMyVector CMyVector::operator+(const CMyVector& mv1)
{
CMyVector retval;
for (int i = 0;i < dim;i++)
{
retval[i] = arr[i] + mv1[i];
}
return retval;
}
CMyVector CMyVector::operator+(const CMyVector mv1)
{
CMyVector *retval = new CMyVector();
for (int i = 0;i < dim;i++)
{
retval[i] = arr[i] + mv1[i];
}
return *this;
}
There are a few issues with this function:
You shouldn't dynamically allocate a CMyVector in order to return by value; that just leaks memory and incurs unnecessary cost. It should just be CMyVector retval;.
Since retval is a pointer, retval[i] is trying to subscript it like an array. You would want (*retval)[i], but since we got rid of the dynamic allocation above, you can just do retval[i].
You return *this instead of *retval. This should now be return retval;.
You should take your argument by-reference to avoid the copy.
operator+ is generally best implemented as a non-member function so that the arguments are treated symmetrically.
The fixed code:
CMyVector operator+(const CMyVector& lhs, const CMyVector& rhs)
{
CMyVector retval;
for (int i = 0; i < dim; i++)
{
retval[i] = lhs[i] + rhs[i];
}
return retval;
}
In the addition operator function, retval is a pointer, which means you must dereference it to use the operator "inline":
(*retval)[i] = ...
Or you could call the operator function explicitly using the "arrow" operator (which does the dereferencing for you):
retval->operator[](i) = ...
However the variable should not be a pointer, since it's the value you should return, which means your operator function is flawed in that it will give you a memory leak and not return the correct value.
See e.g. this operator overloading reference for an example on how to implement it.
Related
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).
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.
I am writing a operator function for - where my class object is a dynamic array of integer.
the operator takes lhs and rhs object and return an object which is the set of elements in lhs but not in rhs.
though I have written the function but I am not able to return the set since the destructor is called right after the object is returned.
IntegerSet & IntegerSet::operator - (IntegerSet & rhs) const
{
IntegerSet temp(capacity);//local object created to store the elements same size as lhs
int k=0;
int lhssize = ElementSize();//no. of elements in the set
int rhssize = rhs.ElementSize();
for (int i=0;i<lhssize;i++)
{
for (int j=0;j<rhssize;j++)
{
if (rhs.ptr[j]!=ptr[i])
{
k++;
}
}
if(k==rhssize)
{
temp = temp + ptr[i];
}
k=0;
}
return temp;
}
and here is the constructor if you cannot understand the object
IntegerSet::IntegerSet(const int & size)//works correctly
{
capacity = size;
ptr = new int [capacity]();
}
IntegerSet::IntegerSet(const int & size)//works correctly
{
capacity = size;
ptr = new int [capacity]();
}
IntegerSet::IntegerSet(const IntegerSet & copy) : capacity(copy.capacity)//works correctly
{
ptr = copy.clonemaker();
}
IntegerSet::~IntegerSet()
{
capacity = 0;
delete [] ptr;
}
int * IntegerSet::clonemaker() const // works correctly
{
if(ptr==NULL)
{
return NULL;
}
int *tempptr = new int [capacity];
for(int i=0;i<capacity;i++)
{
tempptr[i]=ptr[i];
}
return tempptr;
}
You'll have to return by value. The local object will be destroyed when the function returns, and there's no way to prevent that.
For that to work, your class will have to correctly follow the Rule of Three to make sure it's correctly copyable. In C++11 or later, you might also consider making it movable, to avoid unnecessary memory allocation and copying (although, in this case, the copy should be elided anyway).
Better still, follow the Rule of Zero and store a vector<int>, which will do all this for you, rather than trying to juggle raw pointers.
You need to change to return the result by value.
IntegerSet IntegerSet::operator - (IntegerSet & rhs) const
Also it would make more sense to supply rhs by const reference when taking a second look.
Below is the snippet of code where the error lies, the line
a[i][j] = m[i][j] + w[i][j];
returns an error
lvalue required as left operand of assignment
I can't find an answer that applies to arrays, my Matrix is defined as follows:
Matrix::Matrix() {
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
coords[i][j] = 0.0f;
}
const Matrix operator+(const Matrix &m, const Matrix &w) {
Matrix a;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 4 ; j++)
a[i][j] = m[i][j] + w[i][j]; // <----- error
return a;
}
Here is the operator[]
How do I return by reference
const Vector Matrix::operator[](int i) const{
switch(i)
{
case 0: return Vector (coords[i][0], coords[i][1], coords[i][2]);
case 1: return Vector (coords[i][0], coords[i][1], coords[i][2]);
case 2: return Vector (coords[i][0], coords[i][1], coords[i][2]);
}
}
The error is actually "lvalue required as left operand of assignment".
It means that your a[i][j] is giving you a temporary object. This happens when you return from a function by value. A function call that returns by value is an rvalue expression, and you cannot use an rvalue expression as the left operand of an assignment. You need to change the implementation of operator[] on both the Matrix and the helper class that Matrix::operator[] returns so that they return by reference. A function call that returns by reference is an lvalue expression and will allow you to assign to it.
template <typename T>
Vector<T>& Matrix<T>::operator[](int index) { ... }
template <typename T>
T& Vector<T>::operator[](int index) { ... }
Of course, this makes sense. If your operator[]s didn't return by reference, how would assigning to the value returned by them have any effect on the contents of the Matrix?
In response to your edit:
You have a problem with the design of your class. The Matrix class appears to store a 3-by-3 array of floats called coords. However, when you use Matrix::operator[], it copies the values from a row of coords into a Vector object. They are copies. You then return that Vector by value, which copies the Vector and its contained values out of the function. Anything you do to that returned Vector will only affect that copy.
In addition to this, your switch statement is totally pointless. Every case is exactly the same. You just need to use i as the array indices and not switch on it.
Also, if you're going to allow people that call operator[] to modify the contents of your Matrix, then the operator[] function must not be const.
There are a few alternatives that will fix your problem. The first is to just return a float* instead and scrap your plan with Vector:
float* Matrix::operator[](int i) {
return coords[i];
}
That's a very simple solution but does involve passing a raw pointer around. The raw pointer can be used like an array, allowing you the syntax m[i][j].
You could do the same as the Eigen library and instead provide a operator() that takes two arguments, the row index and column index:
float& Matrix::operator()(int i, int j) {
return coords[i][j];
}
Note that the float is being returned by reference: float&. This means that is modifiable from outside the call to operator(). Here, you would index a row and column with m(i, j).
it means your [] operator returns a value without an address that cannot be used for assignment. Its straight forward.
What is Vector?
The problem probably starts with what Matrix::operator[]
returns. For the [][] syntax to work, it must return some
sort of proxy, to which the second [] can be applied, and it
will return a reference to the desired element in the matrix.
The usual way of doing this (at least for me) is to define
a "setter" and "getter" in Matrix:
void set( int i, int j, double new_value )
{
myData[ getIndex( i, j ) ] = new_value;
}
double get( int i, int j ) const
{
return myData[ getIndex( i, j ) ];
}
and then make use of two proxies (nested classes in Matrix):
class Proxy2D
{
Matrix* myOwner;
int myIndex1;
int myIndex2;
public:
Proxy2D( Matrix& owner, int index1, int index2 )
: myOwner( &owner )
, myIndex1( index1 )
, myIndex2( index2 )
{
}
void operator=( double new_value ) const
{
myOwner->set( myIndex1, myIndex2, new_value ):
}
operator double() const
{
return myOwner->get( myIndex1, myIndex2 );
}
};
class Proxy1D
{
Matrix* myOwner;
int myIndex;
public:
Proxy1D( Matrix& owner, int index )
: myOwner( &owner )
, myIndex( index )
{
}
Proxy2D operator[]( int index2 ) const
{
return Proxy2D( *myOwner, myIndex, index2 );
}
};
Proxy1D operator[]( int index1 )
{
return Proxy1D( *this, index1 );
}
In practice, you'll want const variants as well, to be returned
by operator[]( int index1 ) const. And of course,
ConstProxy2D won't need the overload for operator=; the
implicit conversion is sufficient.
And in case it isn't obvious, Matrix::getIndex does the bounds
checking, and then calculates the actual index in the underlying
std::vector<double> which contains the data.
Since you return a newly constructed Vector object to represent a column (or row, doesn't really matter), this Vector is responsible to update an entry in the original Matrix instance, which is a little bit tricky but can easily be solved:
You have to implement another VectorRef class which doesn't represent a vector by value but by reference and can thus be used to access the components as lvalues. This means, that it doesn't contain the numbers as values but as reference and takes these references in its constructor. To keep things simple, you can use a pointer which points to the first component (followed by the other components):
class VectorRef {
float *coords;
public:
VectorRef(float *coords) : coords(coords) {}
float & operator[](int i) { return coords[i]; }
};
Then, construct such a "reference object" in the operator[] of the matrix:
VectorRef Matrix::operator[](int i) {
return VectorRef (coords[i]);
}
To use the VectorRef also as a Vector, provide a conversion operator:
// (within VectorRef:)
operator Vector() const { return Vector(coords[0], coords[1], coords[2]); }
This should make things seemless.
Alternatively, if you always access the elements within a matrix and not columns as a whole, just return the column pointer directly in the operator[] of the matrix:
float * Matrix::operator[](int i) {
return coords[i];
}
When writing mat[x][y], this will first access the float pointer with mat[x] and then accesses the y-th element of that column, which is an lvalue.
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.