a+b+c is not equal to a+(b+c)

G

Gunnar G

Hello.
I'm trying to write a class for integers with a very large number of digits
and it seemed to work until I tried to add three numbers.

d=a+b+c; works fine (hasn't checked the result yet)
but
d=a+(b+c); does not work. ( I have added the parentesis to make sure b+c is
calculated first).
The error I get is:

g++ Bigint.cpp main.cpp && ./a.out
main.cpp: In function `int main(int, char**)':
main.cpp:14: error: no match for 'operator+' in 'c +
Bigint::eek:perator+(Bigint&)((&d))'
Bigint.h:17: error: candidates are: Bigint Bigint::eek:perator+(Bigint&)
/usr/lib/gcc-lib/i686-pc-linux-gnu/3.3.4/include/g++-v3/bits/stl_bvector.h:202:
error:
std::_Bit_iterator std::eek:perator+(int, const
std::_Bit_iterator&)
/usr/lib/gcc-lib/i686-pc-linux-gnu/3.3.4/include/g++-v3/bits/stl_bvector.h:261:
error:
std::_Bit_const_iterator std::eek:perator+(int, const
std::_Bit_const_iterator&)


I assume that there is something very basic thing that I have missed. Here
is the Bigint.h file:

class Bigint{
protected:
vector<int> coef;
bool negative;
public:
void trim();
Bigint(); // sets the integer to 0
Bigint(int x); // sets the integer to x
Bigint operator+(Bigint& x);
friend ostream& operator<<(ostream& out,const Bigint& myint);
};

The Bigint.cpp file is somewhat long, so I dont send it.

Can anyone help me to figure out what is wrong?
 
V

Victor Bazarov

Gunnar said:
I'm trying to write a class for integers with a very large number of digits
and it seemed to work until I tried to add three numbers.

d=a+b+c; works fine (hasn't checked the result yet)
but
d=a+(b+c); does not work. ( I have added the parentesis to make sure b+c is
calculated first).
The error I get is:

g++ Bigint.cpp main.cpp && ./a.out
main.cpp: In function `int main(int, char**)':
main.cpp:14: error: no match for 'operator+' in 'c +
Bigint::eek:perator+(Bigint&)((&d))'
Bigint.h:17: error: candidates are: Bigint Bigint::eek:perator+(Bigint&)
/usr/lib/gcc-lib/i686-pc-linux-gnu/3.3.4/include/g++-v3/bits/stl_bvector.h:202:
error:
std::_Bit_iterator std::eek:perator+(int, const
std::_Bit_iterator&)
/usr/lib/gcc-lib/i686-pc-linux-gnu/3.3.4/include/g++-v3/bits/stl_bvector.h:261:
error:
std::_Bit_const_iterator std::eek:perator+(int, const
std::_Bit_const_iterator&)


I assume that there is something very basic thing that I have missed. Here
is the Bigint.h file:

class Bigint{
protected:
vector<int> coef;
bool negative;
public:
void trim();
Bigint(); // sets the integer to 0
Bigint(int x); // sets the integer to x
Bigint operator+(Bigint& x);

Make this

Bigint operator+(Bigint const& x) const;

In general, every reference argument that is not changing during the
execution of the function should be qualified 'const' and every non-static
member function that doesn't change the object for which it's called is
better declared 'const' too.
friend ostream& operator<<(ostream& out,const Bigint& myint);
};

The Bigint.cpp file is somewhat long, so I dont send it.

Can anyone help me to figure out what is wrong?

Victor
 
K

Karthik Kumar

Gunnar said:
Hello.
I'm trying to write a class for integers with a very large number of digits
and it seemed to work until I tried to add three numbers.

d=a+b+c; works fine (hasn't checked the result yet)
but
d=a+(b+c); does not work. ( I have added the parentesis to make sure b+c is
calculated first).
The error I get is:

g++ Bigint.cpp main.cpp && ./a.out
main.cpp: In function `int main(int, char**)':
main.cpp:14: error: no match for 'operator+' in 'c +
Bigint::eek:perator+(Bigint&)((&d))'
Bigint.h:17: error: candidates are: Bigint Bigint::eek:perator+(Bigint&)
/usr/lib/gcc-lib/i686-pc-linux-gnu/3.3.4/include/g++-v3/bits/stl_bvector.h:202:
error:
std::_Bit_iterator std::eek:perator+(int, const
std::_Bit_iterator&)
/usr/lib/gcc-lib/i686-pc-linux-gnu/3.3.4/include/g++-v3/bits/stl_bvector.h:261:
error:
std::_Bit_const_iterator std::eek:perator+(int, const
std::_Bit_const_iterator&)


I assume that there is something very basic thing that I have missed. Here
is the Bigint.h file:

class Bigint{
protected:
vector<int> coef;
bool negative;
public:
void trim();
Bigint(); // sets the integer to 0
Bigint(int x); // sets the integer to x
Bigint operator+(Bigint& x);

Unless you want to change the values of x, it is
better to write this as -
Bigint operator+(const Bigint& x);


friend ostream& operator<<(ostream& out,const Bigint& myint);
};

The Bigint.cpp file is somewhat long, so I dont send it.

Can anyone help me to figure out what is wrong?

Post main.cpp and the function definition
of the '+' operator overload .
 
J

John Harrison

Unless you want to change the values of x, it is
better to write this as -
Bigint operator+(const Bigint& x);

Even better to write it as this

Bigint operator+(const Bigint& x) const;

And even better still is to write it as a separate function.

DON'T write this

class Bigint
{
Bigint operator+(const Bigint& x) const;
};

DO write this

class Bigint
{
...
};

Bigint operator+(const Bigint& x, const Bigint& y);

operator+ should be a function taking two arguments, it should not be a
member function, usually it will be a friend.

john
 
G

Gunnar G

Alright, here is the code. Please excuse my poor programing style :-(
I could probably improve the code very much.... Note that I added the
"const" that was suggested.
Post main.cpp and the function definition
of the '+' operator overload .

Here is main.cpp
========================================
#include "Bigint.h"
#include <iostream>
#include <string>
using namespace std;

int main(int argc, char* argv[]){
Bigint a,b;
Bigint c(-1003000068);
Bigint d(1234567890);
Bigint e(54564564);
cout<<a<<endl<<b<<endl<<c<<endl<<d<<endl<<d<<endl<<e<<endl;
a=c+e+d;
b=c+(d+d);
cout<<a<<endl<<b<<endl<<c<<endl<<d<<endl<<d<<endl<<e<<endl;
}
==================================
here is the operator+
===================================
Bigint Bigint::eek:perator+(const Bigint& x){
Bigint tmp;
int pos,size;

if (negative && x.negative || !negative && !x.negative){
tmp.negative=negative;
tmp.coef=coef;
while (tmp.coef.size()<x.coef.size()) // padd tmp.
tmp.coef.push_back(0);
tmp.coef.push_back(0);
for (pos=0;pos<x.coef.size();pos++){
tmp.coef[pos]=tmp.coef[pos]+x.coef[pos];
}
tmp.trim();
}
else if (negative){
tmp.coef=coef;
while (tmp.coef.size()<x.coef.size()) // padd tmp.
tmp.coef.push_back(0);
tmp.coef.push_back(0);
for (pos=0;pos<x.coef.size();pos++){
tmp.coef[pos]=-tmp.coef[pos]+x.coef[pos];
}

pos=tmp.coef.size()-1;
while(pos>=0 && tmp.coef[pos]==0)
pos--;
tmp.negative= tmp.coef[pos]<0;
if (tmp.negative)
for (pos=0;pos<tmp.coef.size();pos++)
tmp.coef[pos]=-tmp.coef[pos];
tmp.trim();

}
else if (x.negative){
tmp.coef=coef;
while (tmp.coef.size()<x.coef.size()) // padd tmp.
tmp.coef.push_back(0);
tmp.coef.push_back(0);
for (pos=0;pos<x.coef.size();pos++){
tmp.coef[pos]=tmp.coef[pos]-x.coef[pos];
}

pos=tmp.coef.size()-1;
while(pos>=0 && tmp.coef[pos]==0)
pos--;
tmp.negative= tmp.coef[pos]<0;
if (tmp.negative)
for (pos=0;pos<tmp.coef.size();pos++)
tmp.coef[pos]=-tmp.coef[pos];
tmp.trim();

}
return tmp;
}
 
R

Ron Natalie

Gunnar said:
d=a+(b+c); does not work. ( I have added the parentesis to make sure b+c is
calculated first).

Parenthesis only affect the way things are parsed, it has no bearing on the
order of computation directly.
I assume that there is something very basic thing that I have missed. Here
is the Bigint.h file:

You're going to have to give us more iformation. We need to know
what the line that generates the error is, and what the declarations
of all the types.

We also need to know how you defined operator+.

As near as I can tell the errors don't match either line you gave us.
 
G

Gunnar G

It worked with this change
Bigint operator+(Bigint& x);
to
Bigint operator+(const Bigint& x);

Even more confused now...
 
I

Ivan Vecerina

Gunnar G said:
Hello.
I'm trying to write a class for integers with a very large number of digits
and it seemed to work until I tried to add three numbers. ....
main.cpp:14: error: no match for 'operator+' in 'c +
Bigint::eek:perator+(Bigint&)((&d))' ....
I assume that there is something very basic thing that I have missed. Here
is the Bigint.h file:

class Bigint{
protected:
vector<int> coef;
bool negative;
public:
void trim();
Bigint(); // sets the integer to 0
Bigint(int x); // sets the integer to x
Bigint operator+(Bigint& x);
Parameter x is not being modified, and (*this) neither.
So both should be made const:
Bigint operator+(Bigint& x) const; // but NOT correct yet.

To allow the implicit cast from int to Bigint (using the 2nd ctr)
to apply on both sides of operator+, you also need to declare
binary operators as friend functions.
For instance:
friend Bigint operator+(Bigint& const a, Bigint& const b);
Can anyone help me to figure out what is wrong?

The change above should fix it...

hth-Ivan
 
G

Gunnar G

Even better to write it as this
Bigint operator+(const Bigint& x) const;
And even better still is to write it as a separate function.
Bigint operator+(const Bigint& x, const Bigint& y);
operator+ should be a function taking two arguments, it should not be a
member function, usually it will be a friend.

Why would that be better? I thought the result would be the same? That is,
operator+(a,b) is the same as a+b.
 
V

Victor Bazarov

Gunnar said:
It worked with this change
Bigint operator+(Bigint& x);
to
Bigint operator+(const Bigint& x);

Even more confused now...

The result of

Bigint a,b,c;
a + b + c;

is the same as

Bigint a,b,c;
Bigint some_unnamed_temp( a.operator+(b) );
some_unnamed_temp.operator+(c);

which is fine if your 'operator+' takes a non-const ref (in this
case it's the 'c' object) and is a non-const function (because the
'some_unnamed_temp' is a temporary that is not constant). Now,

Bigint a,b,c;
a + (b + c);

is essentially the same as

Bigint a,b,c;
Bigint some_unnamed_temp( b.operator+(c) );
a.operator+(some_unnamed_temp);

(with one important differece: the 'some_unnamed_temp' is not a real
object, it's a _temporary_ object). And that governs everything.
The operator+ function's argument is a reference to non-const. By
the language rules a non-const reference cannot be bound to a temp
object. That's why it fails. Now if you make the argument 'const',
the binding of 'some_unnamed_temp' works and everything's fine.

Is it better now or worse?

V
 
H

Howard

And even better still is to write it as a separate function.

DON'T write this

class Bigint
{
Bigint operator+(const Bigint& x) const;
};

DO write this

class Bigint
{
...
};

Bigint operator+(const Bigint& x, const Bigint& y);

operator+ should be a function taking two arguments, it should not be a
member function, usually it will be a friend.

Is there anyplace that lists which operators should be friends and which
should be members? I always seem to end up looking at existing code
someplace else when trying to decide, and that's a big waste of time.

Thanks,
Howard
 
G

Gunnar G

(with one important differece: the 'some_unnamed_temp' is not a real
object, it's a _temporary_ object). And that governs everything.
The operator+ function's argument is a reference to non-const. By
the language rules a non-const reference cannot be bound to a temp
object. That's why it fails. Now if you make the argument 'const',
the binding of 'some_unnamed_temp' works and everything's fine.

Is it better now or worse?
Much better! Thank you all, especially Victor for this explanation.
Now I can go to sleep without nightmares!
 
V

Victor Bazarov

Howard said:
[..]
Is there anyplace that lists which operators should be friends and which
should be members? I always seem to end up looking at existing code
someplace else when trying to decide, and that's a big waste of time.

IIRC, it's called "Effective C++". Probably, Item 19. Don't have my copy
here, sorry.

Victor
 
I

Ivan Vecerina

And as I wrote earlier, this function can be declared within
the class itself as a friend (as you did for streaming operator <<).
Why would that be better? I thought the result would be the same? That is,
operator+(a,b) is the same as a+b.
Not quite the same.
Consider:
Bigint a;
int b;

Bigint c = a+b; // works in either case
Bigint d = b+a; // only works if operator+ is a friend !

When operator+ is a member function, implicit conversion of int
to Bigint will not be applied to the left-hand-side argument.

.... as previously posted.

Cheers,
Ivan
 
I

Ivan Vecerina

Howard said:
Is there anyplace that lists which operators should be friends and which
should be members? I always seem to end up looking at existing code
someplace else when trying to decide, and that's a big waste of time.
It is often a matter of style or choice.

The constraints I can think of are:

Binary operators of class C must be NON-members when:
- the lhs (left hand side) argument is not of type C
- the lhs is of type C or const& C, but other types that are implicitly
convertible to C should be allowed to.
I am among those who will prefer to use non-member functions whenever
possible (and not only for operator functions). That is, whenever
access to private data members is not needed.

This said, some operators must always be member functions,
in particular: = -> and implicit conversions.
And while the += -= etc operators can be non-members, it often
makes sense to declare them as members, as well as operator=
(it doesn't make sense to allow implicit conversions for the lhs anyway)

hth,
Ivan
 
I

Ivan Vecerina

Victor Bazarov said:
Howard said:
[..]
Is there anyplace that lists which operators should be friends and which
should be members? I always seem to end up looking at existing code
someplace else when trying to decide, and that's a big waste of time.

IIRC, it's called "Effective C++". Probably, Item 19. Don't have my copy
here, sorry.
Yes, and the conclusion of the item is:

a.. Virtual functions must be members. If f needs to be virtual, make it a
member function of C.
a.. operator>> and operator<< are never members. If f is operator>> or
operator<<, make f a non-member function. If, in addition, f needs access to
non-public members of C, make f a friend of C.
a.. Only non-member functions get type conversions on their left-most
argument. If f needs type conversions on its left-most argument, make f a
non-member function. If, in addition, f needs access to non-public members
of C, make f a friend of C.
a.. Everything else should be a member function. If none of the other cases
apply, make f a member function of C.

However, many will disagree with the last statement: if one of the
other function does not need access to private data members,
it might be a better idea (for maintenance and clarity) to keep it
as a non-member function. Especially for non-operator functions,
where this impacts the calling style.

E.g. for a vector class, I much prefer the consistent syntax:
norm(v) angle(v1,v2) cross(v1,v2)
to:
v.norm() v1.angle(v2) v1.cross(v2)

Of course YMMV.

Ivan
 
V

Victor Bazarov

Ivan said:
Howard said:
[..]
Is there anyplace that lists which operators should be friends and which
should be members? I always seem to end up looking at existing code
someplace else when trying to decide, and that's a big waste of time.

IIRC, it's called "Effective C++". Probably, Item 19. Don't have my copy
here, sorry.

Yes, and the conclusion of the item is:

a.. Virtual functions must be members. If f needs to be virtual, make it a
member function of C.
a.. operator>> and operator<< are never members. If f is operator>> or
operator<<, make f a non-member function. If, in addition, f needs access to
non-public members of C, make f a friend of C.
a.. Only non-member functions get type conversions on their left-most
argument. If f needs type conversions on its left-most argument, make f a
non-member function. If, in addition, f needs access to non-public members
of C, make f a friend of C.
a.. Everything else should be a member function. If none of the other cases
apply, make f a member function of C.

However, many will disagree with the last statement: if one of the
other function does not need access to private data members,
it might be a better idea (for maintenance and clarity) to keep it
as a non-member function. Especially for non-operator functions,
where this impacts the calling style.

E.g. for a vector class, I much prefer the consistent syntax:
norm(v) angle(v1,v2) cross(v1,v2)
to:
v.norm() v1.angle(v2) v1.cross(v2)

I believe the difference you're talking about is design-driven, not style
or implementation-driven.

Of course, there is always some kind of operator analogy that can be used
here. For example, you might want to define operator^ for angle() and
operator*() for cross. In that case it's possible that you want them as
non-members because you might want conversions applied. Now, if you turn
back the switch and drop the operator-ness of them, they seem better non-
members.

Another possible solution is to make non-members actually static. In that
case you write

vector::norm(v) vector::angle(v1,v2) vector::cross(v1,v2)

which keeps it a bit better recognised, although a bit more difficult to
type.

Victor
 
E

E. Mark Ping

Bigint operator+(const Bigint& x, const Bigint& y);

operator+ should be a function taking two arguments, it should not be a
member function, usually it will be a friend.

Ugh. Much better to write operator+= and then implement operator+
in terms of that.


So:

class Bigint
{
//...

public:
Bigint operator+=(const Bigint& other);
};


Bigint operator+(const Bigint& lhs, const Bigint& rhs)
{
return (Bigint(lhs)+=rhs);
}
 
D

DaKoadMunky

Yes, and the conclusion of the item is:
a.. Virtual functions must be members. If f needs to be virtual, make it a
member function of C.
a.. operator>> and operator<< are never members. If f is operator>> or
operator<<, make f a non-member function. If, in addition, f needs access to
non-public members of C, make f a friend of C.
a.. Only non-member functions get type conversions on their left-most
argument. If f needs type conversions on its left-most argument, make f a
non-member function. If, in addition, f needs access to non-public members
of C, make f a friend of C.
a.. Everything else should be a member function. If none of the other cases
apply, make f a member function of C.

However, many will disagree with the last statement: if one of the
other function does not need access to private data members,
it might be a better idea (for maintenance and clarity) to keep it
as a non-member function.

In a 2000 article Scott Meyers updated this formula in an article entitled "How
Non-Member Functions Improve Encapsulation" to split the "everything else
should be a member function" case into two cases for reasons similar to what
Ivan ponted out.

http://www.cuj.com/documents/s=8042/cuj0002meyers/meyers.htm

I think it is a neat read and it creates a definition of encapsulation that is
most useful.
 
J

John Harrison

Gunnar G said:
Why would that be better? I thought the result would be the same? That is,
operator+(a,b) is the same as a+b.

Not the same in all situations. Consider this pseudo code (this is the wrong
way)

class BigInt
{
BigInt(int x);
BigInt operator+(const BigInt& x) const;
};

BigInt x(1);
BigInt y = 2 + x; // error
BigInt z = x + 2; // ok

Now consider this (this is the right way)

class BigInt
{
BigInt(int x);
};

BigInt operator+(const BigInt& x, const BigInt& y);

BigInt x(1);
BigInt y = 2 + x; // ok
BigInt z = x + 2; // ok

The difference is the automatic conversions that the compiler is allowed to
make. BigInt has an automatic conversion from int because there is a
constructor that takes one int argument. The compiler will always use this
on a function or method parameter, that is why x + 2 always compiles, and
its why 2 + x compiles in the second case. But the compiler will not perform
this automatic conversion when the object is the object on which a method is
called. So the compiler does not convert the 2 in 2 + x when operator+ is a
member function. That is why operator+ or anything similar should not be a
member function. On the other hand operator += must operate on a BigInt, you
wouldn't want 2 += x to compile, so operator += should be a member function.

John
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top